The IntelliJ Platform is not a product in and of itself but provides a platform for building IDEs. It is used to power JetBrains products such as IntelliJ IDEA (https://www.jetbrains.com/idea/). It is also Open Source and can be used by third parties to build IDEs, such as Android Studio (https://developer.android.com/studio/index.html) from Google.
The IntelliJ Platform provides all the infrastructure that these IDEs need to provide rich language tooling support. It is a component-driven, cross-platform JVM based application host with a high-level user interface toolkit for creating tool windows (Tool Windows), tree views, and lists (supporting fast search) as well as popup menus and dialogs (Dialogs).
The IntelliJ Platform has a full-text editor (Editors) with abstract implementations of syntax highlighting (Analyzing), code folding (12. Folding Builder), code completion (Code Completion), and other rich text editing features (Editing). An image editor is also included.
Furthermore, it includes open APIs to build standard IDE functionality, such as a project model (Project) and a build system (External System Integration). It also provides an infrastructure for a rich debugging experience, with language-agnostic advanced breakpoint support, call stacks, watch windows, and expression evaluation.
But the IntelliJ Platform's real power comes from the Program Structure Interface (PSI (Program Structure Interface (PSI))). It is a set of functionalities used to parse files, build rich syntactic and semantic models of the code, and build indexes (Indexing and PSI Stubs) from this data. PSI powers a lot of functionalities, from quick navigating to files (PSI Files), types, and symbols (Symbols), to the contents of code completion (Code Completion) popups and find usages (Find Usages), code inspections (Code Inspections), and code rewriting, for quick fixes or refactorings (Rename Refactoring), as well as many other features.
The IntelliJ Platform includes parsers (Implementing Parser and PSI) and a PSI model for many languages, and its extensible nature means that it is possible to add support for other languages (Custom Language Support).
Products built on the IntelliJ Platform are extensible applications, with the platform being responsible for creating Extensions. The IntelliJ Platform fully supports plugins (Developing a Plugin), and JetBrains hosts the JetBrains Marketplace (https://plugins.jetbrains.com), which can be used to distribute plugins that support one or more of the products. It is also possible to distribute plugins using a Custom Plugin Repository.
Plugins can extend the platform in many ways, from adding a simple menu item to adding support for a complete language, build system, and debugger. Many of the existing IntelliJ Platform features are implemented as plugins that can be included or excluded depending on the needs of the end product. See the Quick Start Guide for more details.
Plugin AlternativesIn some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as alternative solutions (Alternatives to Implementing a Plugin) exist.
The IntelliJ Platform is Open Source, under the Apache License (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/LICENSE.txt), and hosted on GitHub (https://github.com/JetBrains/intellij-community).
While this guide refers to the IntelliJ Platform as a separate entity, there is no "IntelliJ Platform" GitHub repository. Instead, the platform is considered to be an almost complete overlap with the IntelliJ IDEA Community Edition (IntelliJ IDEA Plugin Development), which is a free and Open Source version of IntelliJ IDEA Ultimate (the GitHub repository linked above is the JetBrains/intellij-community (https://github.com/JetBrains/intellij-community) repository). Please note: starting with the 2021.1 release, some plugins bundled with IntelliJ IDEA Community Edition are not open-source.
The version of the IntelliJ Platform is defined by the version of the corresponding IntelliJ IDEA Community Edition release. For example, to build a plugin against IntelliJ IDEA (2019.1.1), build #191.6707.61 means specifying the same build number tag to get the correct IntelliJ Platform files from the intellij-community repository. See the Build Number Ranges page for more information about build numbers corresponding to version numbering.
Typically, an IDE that is based on the IntelliJ Platform will include the intellij-community repository as a Git submodule and provide configuration to describe which plugins from the intellij-community, and which custom plugins will make up the product.
The IntelliJ Platform underlies many JetBrains IDEs. IntelliJ IDEA Ultimate (IntelliJ IDEA Ultimate) is a superset of the IntelliJ IDEA Community Edition but includes closed-source plugins (see this feature comparison (https://www.jetbrains.com/idea/features/editions_comparison_matrix.html)). Similarly, other products such as WebStorm (WebStorm Plugin Development) and DataGrip (DataGrip Plugin Development) are based on the IntelliJ IDEA Community Edition, but with a different set of plugins included and excluding other default plugins. This allows plugins to target multiple products, as each product will include base functionality and a selection of plugins from the IntelliJ IDEA Community Edition repository.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The following IDEs are based on the IntelliJ Platform:
JetBrains (https://www.jetbrains.com) IDEs:
AppCode (AppCode Plugin Development)
CLion (CLion Plugin Development)
DataGrip (DataGrip Plugin Development)
GoLand (GoLand Plugin Development)
IntelliJ IDEA (IntelliJ IDEA Plugin Development)
PhpStorm (PhpStorm Plugin Development)
PyCharm (PyCharm Plugin Development)
Rider
RubyMine (RubyMine Plugin Development)
WebStorm (WebStorm Plugin Development)
Android Studio (Android Studio Plugin Development) IDE from Google
Comma (https://commaide.com/) IDE for Raku (formerly known as Perl 6)
Jmix Studio (https://www.jmix.io/tools/)
JetBrains Rider (Rider Plugin Development) uses the IntelliJ Platform differently than other IntelliJ-based IDEs. It uses the IntelliJ Platform to provide the user interface for a C# and .NET IDE, with the standard IntelliJ editors, tool windows, debugging experience, etc. It also integrates into the standard Find Usages and Search Everywhere UI and uses code completion, syntax highlighting, and so on.
However, Rider doesn't create a full PSI (Program Structure Interface (PSI)) (syntactic and semantic) model for C# files. Instead, it reuses ReSharper (https://www.jetbrains.com/resharper/) to provide language functionality. All the C# PSI model, inspections, code rewritings, such as quick fixes, and refactorings are run out of the process, in a command-line version of ReSharper. This means that creating a plugin for Rider involves two parts - a plugin that lives in the IntelliJ "front end" to show user interface, and a plugin that lives in the ReSharper "back end" to analyze and work with the C# PSI.
Fortunately, many plugins can simply work with the ReSharper backend. Rider takes care of displaying the results of inspections and code completion, and many plugins can be implemented without requiring an IntelliJ UI component.
Please make sure to read the Code of Conduct (Code of Conduct).
There are several community forums and newsgroups (https://intellij-support.jetbrains.com/hc/en-us/community/topics) you can join to discuss the IntelliJ Platform and IDEs. The forums are an excellent source for users and contributors interested in having technical discussions, answering questions, or resolving potential issues for newcomers.
The success of any open-source project depends on the number of people who use the product and contribute back to the project. By linking to https://www.jetbrains.com/opensource/idea (https://www.jetbrains.com/opensource/idea), you can increase the chances of a new user or contributor finding out about the project and joining the community.
If you're as excited about IntelliJ IDEA Community Edition as we are, you can show it by linking to us. Project logos and other assets are also available (https://www.jetbrains.com/company/brand/logos/).
Help promote the platform and IDE by using your blog, Twitter, Facebook, or submitting an article to your favorite local magazine. If you are a member of a different open-source community, why not mention IntelliJ IDEA on their discussion forums or at conferences? If you love IntelliJ IDEA, don't hold back — speak up! The more developers use IntelliJ IDEA, the more bugs will be caught, the more plugins will be written, the more visible the project, and the more benefits the community will get!
We're always looking for new articles about IntelliJ IDEA features as well as documentation for the IntelliJ Platform. You can write tutorials, how-tos, sample applications, or share your experience with the IntelliJ Platform. You can publish your documentation on a website or blog, or submit a pull request (Contributing to the IntelliJ Platform SDK) to the SDK Docs.
Screencasts have recently become very popular as a way to show other developers how to use the tool effectively. You can record a screencast about a particular feature or use case you discovered and would like to share it with the community.
Bug reports take little time to file and are very helpful to developers. This is one of the easiest contributions you can make. When you discover a problem with the IDE or the platform, please report it. Make sure you provide information about your environment (available from About menu), steps to reproduce the issue, as well as a written description of the problem. You can file a bug in our YouTrack issue tracker (https://youtrack.jetbrains.com/issues/IDEA). Before submitting an issue, please search for already submitted ones describing the same problem — and if you find one, feel free to vote for it.
Over the years, users have submitted thousands of issues to the IntelliJ IDEA issue tracker. Many of the unresolved issues are no longer applicable to the latest version of IntelliJ IDEA, are duplicates, or require additional information to be resolved. Leaving comments notifying about the status of such issues helps the team keep the issue tracker clean and useful for everyone.
One of the best ways to contribute a larger piece of code, adding extra functionality to IntelliJ IDEA or any of the other IntelliJ Platform-based IDEs, is by writing a plugin. You can submit a plugin to the JetBrains Marketplace (https://plugins.jetbrains.com/), making it available for all users. When writing a plugin, you have control over the code and don't need to sign the contribution agreement.
If you would like to improve the code in the IntelliJ Platform or the core functionality of IntelliJ IDEA, you can submit a pull request to the IntelliJ IDEA Community Edition repository on GitHub (https://github.com/JetBrains/intellij-community). When preparing the change, please make sure to follow the IntelliJ Platform Coding Guidelines. A developer will review your contribution and, if it meets the quality criteria and fits well with the rest of the code, you'll be notified about the acceptance of the patch.
Looking for issues to work on? Issues marked with #patch_welcome (https://youtrack.jetbrains.com/issues/IDEA?q=%23patch_welcome%20%23unresolved) tag are looking for external contributors.
Alternatively, you can attach a patch to the ticket in the YouTrack bug database (https://youtrack.jetbrains.com/issues/IDEA). You can either file a new issue with the patch attached or attach a patch to an issue submitted by another user. In this case, you will also need to sign the JetBrains Contributor License Agreement (CLA) (https://www.jetbrains.com/agreements/cla/) to complete your contribution.
Developers with a long history of submitting high-quality patches can gain direct commit rights.
If you are writing code that you would like to contribute to the IntelliJ Platform, following these guidelines will make it easier for the JetBrains development team to review and accept your changes.
If you submit patches, we strongly recommend building your patches against the latest version of the code from the intellij-community Git repository ("Open Source" in "The IntelliJ Platform"). The easiest way to do so is to clone the repository, track your work in Git, and provide your changes as described in "Submit a Patch" in "Contributing to the IntelliJ Platform".
Please do your best to follow common Java architectural principles. "Effective Java" by Joshua Bloch is the right place to start.
Functional tests cover most of the existing functionality of IntelliJ IDEA. If tests cover the area you're modifying, you must run the tests and make sure that your changes do not introduce any new test failures. It's also strongly recommended that you provide new functional tests that cover the bugs you fix or the new features that you add.
We're generally pretty lax about code formatting, but at least the following conventions must be observed:
2 space indents in source files
Use my prefix for instance variables and our prefix for class variables.
New source code files must include a copyright statement with the Apache 2 license and the name of the contributor.
The easiest way to follow our code formatting guidelines is to reformat your code submissions using the shared code style, which is included in the IntelliJ IDEA Community Edition project directory.
The IntelliJ IDEA Community Edition project includes a shared inspection profile. We strongly recommend making sure that the code you submit does not contain any warnings highlighted by the inspections configured in that inspection profile.
If your code adds new OpenAPI interfaces, classes, methods, or extension points, you must provide Javadoc comments describing the parameters and intended usage of the APIs. Providing Javadoc or other comments for other parts of the code is a good idea but isn't required.
To avoid unnecessary work when reviewing your changes, please follow these guidelines:
Look through all of your changes in your patch or pull request before you submit it to us. Make sure that everything you've changed is there for a reason.
Please don't include unfinished work in the patch. Make sure that it doesn't contain any TODO comments. If you added some code and ended up not needing it, please make sure that you delete it before you submit your patch.
Please don't include any changes that affect formatting, fixing "yellow code" (warnings), or code style along with actual changes that fix a bug or implement a feature. No one likes to leave poor code, but remember that having these changes mixed complicates the process of review.
Please don't fix multiple problems within a single patch or pull request.
Please don't commit your changes to configuration files (runConfigurations/IDEA.xml, codeStyleSettings.xml, misc.xml, etc.) unless it is essential for the fix itself.
Please avoid moving or renaming code unless it is necessary for the fix. Keeping backwards compatibility is critical for the platform.
The ideal pull request would contain one commit with everything needed to fix the bug or implement a feature, but nothing else. "Commit early, commit often" perfectly applies only to local commits, but such "public commits" are hard to review (the reviewer needs either to go commit by commit spending more time to review work-in-progress, or to review all changes at once thus losing valuable information stored in commit messages).
The best would be to commit early, but then to squash all commits into one with a descriptive commit message.
Sometimes several commits for a single issue are also acceptable, but each of these should be self-contained "steps" to solve the problem.
This guide is split into several parts, similar to a textbook. Each one builds on the content of the previous section, but it is not necessary to read the guide in order. The Key Topics (Key Topics) page aims to link to the pages that are necessary to be able to understand the architecture and get started building plugins.
All source links and reference lists target IntelliJ Platform 2024.1.
While browsing this guide, you will notice that there are topics that are greyed out. Unfortunately, the guide is not complete and contains placeholders for specific topics. We are working on increasing the coverage, but if you get stuck due to missing content, please see the Getting Help section for details on how to get moving again.
The guide is also Open Source on GitHub (https://github.com/JetBrains/intellij-sdk-docs), and Pull Requests for new content, corrections or updates are always gratefully received. Please see the Contributing (Contributing to the IntelliJ Platform SDK) page for details.
See also Glossary for a handy reference of common terms.
Describes how to create a plugin that can extend the IntelliJ Platform. Includes details on how to set up the project, register extension points, target specific versions of the IntelliJ Platform, and how to package, deploy, and test your plugins.
Describes the foundational layer of the architecture, which provides many features and utilities, such as the component model, the user interface, documents and editors, the virtual file system, settings, threading, and background tasks. The Base Platform layer mainly comprises the functionality of the IntelliJ Platform that does not target language features or parsing.
Documents the Project Model, which represents the files and configuration of the currently loaded project, as well as the build system used to build the project.
The Program Structure Interface (PSI) builds the syntactic and semantic models for lots of different file types. This section describes how to work with the PSI, navigating and manipulating the syntax trees, and also looks at the powerful references system, which allows a syntax tree node to reference an item in the semantic model. It also details how PSI creates and uses indexes.
Describes how to extend and interact with various features that use the PSI layer, such as code completion, navigation, AltEnter items, intentions, refactorings, and more. See also the section on Custom Languages below for language-specific features that are only applicable when adding support for a new language.
Describes the available infrastructure for writing automated tests covering the functionality of plugins.
Plugins frequently extend support for existing languages, such as adding inspections to Java files. This section describes how to add support to the IntelliJ Platform for a new language that isn't supported by default, creating parsers, syntactic and semantic models, and all the features that build on top.
A lot of the functionalities in the IntelliJ Platform are language and product agnostic. For example, code inspections work the same in Java as they do in Ruby; it is just the syntax trees and semantic information that is different. This section describes product-specific features, such as specific project model differences and how to target them in a plugin.
Documents how to use the IntelliJ Platform to create a new, custom IDE, rather than plugins to an existing product, e.g., WebStorm, or Android Studio.
Describes how to create a theme for IntelliJ Platform-based IDEs. Includes details on how to set up the theme project, customize, build, and publish it on JetBrains Marketplace.
Links to useful resources (Useful Links), a Glossary, IntelliJ Platform Extension Point and Listener List, tips on how to Explore the IntelliJ Platform API and Learning Resources.
Information on Verifying Plugin Compatibility and list of backwards-incompatible (Incompatible Changes in IntelliJ Platform and Plugins API) API changes as well as notable changes and new features (Notable Changes and Features in IntelliJ Platform and Plugins API) in each major release of the IntelliJ Platform.
Reference and usage guides for commonly used tools like the Gradle IntelliJ Plugin.
The IntelliJ Platform is extensive and very capable, and its size and scope can initially be very daunting. This page is intended to list the key topics that a plugin author would be interested in, and provide quick links to the most common extension points.
Component model - the IntelliJ Platform is a component-based application and is responsible for creating components and injecting dependencies. Understanding this is necessary for building plugins.
Extension Points (Extensions) - how to register components with extension points, and how to find out what extension points are available.
Virtual Files - all file access should go through the Virtual File System, which abstracts and caches the file system. It means you can work with files that are on the local file system, in a zip file or are old versions from version control.
See also Glossary for a handy reference of common terms.
The IntelliJ Platform's code model is called the PSI - the Program Structure Interface (Program Structure Interface (PSI)). The PSI parses code, builds indexes, and creates a semantic model.
The IntelliJ Platform is extremely extensible, and most features and services can be extended. Some common extension points are:
Actions - menu and toolbar items
Code Inspections - code analysis that looks at the syntax trees and semantic models and highlight issues in the editor.
Intentions - context-specific actions that are available in the AltEnter menu when the text caret is at a particular location.
This document describes our contribution guidelines for the open-source IntelliJ Platform SDK documentation and sample code. Before you begin contributing content to the SDK, please read this page thoroughly as well as the Code of Conduct (Code of Conduct) and License (https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents.
For information about contributing to the IntelliJ Platform itself, please visit Contributing to the IntelliJ Platform.
Content contributions to the IntelliJ Platform SDK are very welcome!
Before creating content for a new topic or adding large sections to the existing pages, please create an issue in the IJSDK YouTrack project (https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=) to ensure we are not working on the same topic, and to agree on the form of the content draft. Checking with us before starting the work will reduce the risk of creating content that is unnecessary and won't be accepted.
Please clone the intellij-sdk-docs project from GitHub (https://github.com/JetBrains/intellij-sdk-docs), make additions or changes, and submit a pull request. Alternatively, start by clicking on the Edit page link on the top of each page.
Before creating or altering content, please consult these guides:
SDK Docs Style Guide. This guide describes documentation conventions in terms of content style and syntax.
SDK Code Sample Guidelines. Conventions for code sample organization, project settings, and naming conventions are described in this document.
Currently, building the site locally is not possible.
This document describes the writing style used in authoring open-source IntelliJ Platform SDK documentation. Before you begin, please read this page thoroughly, as well as the Code of Conduct (Code of Conduct) and License (https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents. Please see also Contributing to the IntelliJ Platform SDK for some general remarks. For information about contributing to the IntelliJ Platform itself, please visit Contributing to the IntelliJ Platform (Contributing to the IntelliJ Platform).
First and foremost, we should keep in mind our audience and their objectives: Someone reading technical content is usually looking to answer a specific question. That question might be broad or narrowly-focused, but either way, our goal is to provide answers without distraction.
The style of the IntelliJ Platform SDK documentation is captured by using a markup language named Markdown (https://github.github.com/gfm/).
To verify grammar and correct spelling, it is highly recommended to use Grazie Professional (https://plugins.jetbrains.com/plugin/16136-grazie-professional) plugin to highlight any issues on-the-fly in the IDE.
The documentation project is using Writerside (https://www.jetbrains.com/writerside), so the plugin should be installed to have full support in the IDE. The topic files themselves are Markdown (https://github.github.com/gfm/) files (*.md) using some Writerside-specific custom tags (see below).
Each Markdown file must start with a copyright notice, formatted using HTML comment notation:
<!-- Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->It must be followed by a header defining its title using the level 1 heading:
# Contributing to the IntelliJ Platform SDKThe page title should be as concise as possible, so it can be reused in the as is.
Every page should provide a short excerpt (usually one sentence) using dedicated <link-summary> tag before the main page contents:
<link-summary>Defining groups of related settings.</link-summary>A page can highlight related topics and other important links before the actual content using <tldr> tag. Links must be grouped using " Bold Category Name: link1, link2, \[...]" (Example (2. Language and File Type)).
Use Reference to link to other topics, Code to link to code/files, Platform UI Guidelines for links to IntelliJ Platform UI Guidelines (https://jetbrains.design/intellij/), and Product Help for links to IntelliJ IDEA Help (https://www.jetbrains.com/help/idea).
Do not use headings like Introduction, Overview, etc. for any introductory text.
Consistent terminology helps the reader grasp new concepts more quickly:
The open-source code in the GitHub repository intellij-community is known as the IntelliJ Platform. Use the full phrase in the SDK documentation.
IDEs based on the IntelliJ Platform are described as IntelliJ Platform-based IDEs. Once that term is used on a page, authors may use IDEs.
When referring to JetBrains products always use the full name such as IntelliJ IDEA Ultimate Edition. However, only use product names when extensibility or functionality is particular to a product.
Do not use TODOsTODO/todo comments are discouraged in the main branch of intellij-sdk-docs. There are always exceptions, but the best practice is to resolve all TODOs before the final review. If immediate resolution isn't possible, write a YouTrack SDK Issue (https://youtrack.jetbrains.com/issues/IJSDK) that captures the TODO and remove the comment from the document.
Start every sentence on a new line. For very long sentences, add additional line breaks after ,, : or other sensible places. Very long links should also be on a separate line.
Consistent text styles are used to standardize references and keywords:
Menu paths are wrapped using <ui-path> with pipe characters separating each level: <ui-path>Settings | Editor</ui-path>: Settings | Editor Inside tables, use | instead of | to prevent escaping problems.
User interface element names like labels, buttons, checkboxes, etc. are wrapped using <control>: Press <control>Continue</continue>: Press Continue
Non-code keywords and quotations, or the names of non-code files are formatted as italic style: _Theme_ (Theme), _README.md_ (README.md.) Examples of this file type include LICENSE.txt and README.md.
Code keywords and classnames are formatted as code style: \`interface\`: interface, \`AnAction\`: AnAction, \`name\` attribute: name attribute.
Filenames are wrapped using <path>: <path>build.gradle.kts</path> build.gradle.kts.
File formats are shown as all capital letters: PNG and XML.
Filename extensions are not capitalized when part of a full filename, path, or URL: plugin.xml.
Keyboard shortcuts are wrapped using <shortcut>: press <shortcut>Alt+Insert</shortcut> becomes "press AltInsert ".
See Guidelines for Highlighting Syntax for best practices for representing code fragments.
See Links to IntelliJ Platform Source for more details about representing names of source files in links.
Links are handled as standard Markdown links and can be anchored to external sites, pages within the site, or headings in the pages.
When a Markdown header is converted to an HTML header, it is assigned an ID so that it can be linked. For example, ## Basics gets the ID of basics, and can be linked either on the same page or cross-page as described below.
In some cases (e.g., the same heading text appears multiple times on the same page), it is necessary to specify a distinct ID manually:
## Task 1
### Properties
{#task1-properties}
[...]
## Task 2
### Properties
{#task2-properties}
[...]General Markdown links have the default Markdown link style:
[Gradle](https://gradle.org) (Gradle (https://gradle.org)) links to an external site, such as companies, articles, etc. If URL contains % character, append {ignore-vars="true"}.
Linking to pages and page sections within the SDK documentation:
[Page Title](page.md) or [](page.md) (use page title as link text) links to an SDK doc page (all located under /topics). Note that the extension is .md, NOT .html.
Specific sections on pages in the SDK documentation are linked by using section anchors. The anchor name will be all lower case, and spaces are replaced with -, e.g. ## Page setup becomes #page-setup. Once the anchor (#) character of the link is entered, the IDE code completion feature shows the available sections.
[Link to a section on the current page](#another-section) links to a heading on the current page.
[Link to the section on another page](other_page.md#another-section) links to a heading on another page.
If the desired link label is the same as an SDK doc page or section title, leave the label part empty, e.g., [](test-page.md) or [](test-page.md#section-1). The empty link label will be automatically filled with the actual page or section title.
Links to files in the IntelliJ Platform (intellij-community) repository use %gh-ic% prefix instead of the full URL to the repository. Links to files in source code packages in other GitHub repositories follow much the same rules, except the links use a different custom gh-... prefix defined in v.list.
[README.md](%gh-ic%/README.md) links to general, non-code information files. (README.md (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/README.md)) Examples of this file type include LICENSE.txt and README.md.
[IdeaPlugin.xml](%gh-ic%/community-resources/resources/META-INF/IdeaPlugin.xml) links to declarative source code files, use code style. (IdeaPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/community-resources/resources/META-INF/IdeaPlugin.xml)) Examples of this file type include: settings.gradle, plugin.xml or theme_basics.theme.json.
[ \`AnAction\`](%gh-ic%/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java) links to source files for code objects like interfaces and classes, use code style but without the file extension. (AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java)) Examples of this file type include Java and Kotlin sources.
Note the use of \`\` characters surrounding the class name in the link.
When linking to an API in this manner, the FQN isn't necessary in the link.
No file extension (*.java, *.kt, *.py, etc.) is used by convention.
Be judicious when using such links. Generally, only one link is needed for a given file on a documentation page.
In-paragraph code fragments and IntelliJ Platform APIs are formatted according to the following rules.
Avoid using qualifiers like " Foo interface" or " Foo abstract class". Instead, refer to Foo.
The FQN is used for the first reference to an interface, class, or package on a page. Rather than AnAction, introduce it as com.intellij.openapi.actionSystem.AnAction. Subsequent references on the page can be AnAction. Exception: the FQN is not used with a GitHub link.
Method names always use empty parentheses: "call bar() to apply." Method names are prefixed with the class/interface name when needed for clarity: Foo.bar().
Extension point name must be followed by "extension point (EP)" for the first occurrence on a page. All following can use "EP" suffix.
Use the FQN when first introducing an extension point (Extension Points) (EP) on a page. Rather than stubIndex, introduce com.intellij.stubIndex. Subsequent mentions on the page can be stubIndex.
For XML elements, use the tag notation with syntax highlighting: <idea-version>. Attributes are shown with syntax highlighting, and attribute values are shown in quotes: since-build="191"
Source code is represented by using code fences, which are three backticks.
Syntax highlighting is applied by specifying the language after the first set of ticks:
```xml
<tagName attribute="value">XML Text</tagName>
```Supported languages include xml, java, kotlin, groovy, bash, md, php, and text for plaintext.
Source code blocks must have one blank line before and after them, and must have a language specification for highlighting (use text as fallback).
Whole files can be imported on a page using src attribute after code fences specifying the full path relative to code_samples root folder.
{src="simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFoldingBuilder.java"}
The advantage is the code can come from the code_samples directory, so it will be live code that isn't silently stale.
The disadvantage is the file may contain a large class, too large for the documentation page to be useful. If possible, use include-symbol="ClassName" to show only the class body without any headers and imports. To include only a specific method, specify include-symbol="methodName" additionally.
In any case, please keep code samples concise and avoid any unnecessary "surrounding" code or import statements.
The syntax is to use the pipe (|) and minus symbols:
| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Blah | Blah | Blah |Use & #124; (remove space before #!) instead of | to prevent escaping problems for <ui-path> elements inside cells.
Use <p>Line 1</p><p>Line 2</p> for multi-line content in a cell.
Notes and callouts can be specified using the blockquote syntax. The converter looks at the type attribute specified after the text block. If so, it applies a callout style. The example below will be displayed as a callout, styled as a "note":
> This is a simple note.
>
{style="note"}This is a simple note.
The styles available for callouts are:
tip—Information that makes the reader more productive (Default).
note—Information that is important for the reader to understand. This callout is reserved for essential points and concepts.
warning—Information that is critical for the user to understand to prevent failures or errors.
Complex callouts can also specify title attribute:
> This is a note.
> We have a lot of text.
> Don't make everyone read it fully by adding a good title.
>
{title="A useful title"}A useful titleThis is a note. We have a lot of text. Don't make everyone read it fully by adding a good title.
Every page typically has a dedicated subdirectory within root /images.
Images in this documentation are generally screenshots. For consistency, images should be 296, 460, or 706 pixels wide. The preferred image format is PNG at 144 DPI resolution. A resolution of 72 DPI is acceptable but may look blurry on high-resolution monitors.
Use Window Resizer (https://plugins.jetbrains.com/plugin/18045-window-resizer) plugin for exact resizing of the IDE application window.
It is crucial to reduce the size of image files to prevent bloating the repository and impacting the performance of the documentation site. Optimize the image files using a tool such as the PNG optimizer (https://plugins.jetbrains.com/plugin/7942-png-optimizer) plugin.
Images are embedded in a document by adding a Markdown link to the image like so:
If the width of an image needs to be adjusted, it can be specified as follows:
{width="42"}Images too big to fit into main content can have + overlay control to open a popup with the "zoomed" variant.
For PNG images, provide an additional zoomed variant image.zoomed.png with this notation:
{thumbnail="true"}For SVG images, use this notation:
{thumbnail-same-file="true"}The table of contents for the site is displayed in the tree view on the left side of the site, and it is generated from the ijs.tree file. The list can have nested items, which are displayed as child items in the table of contents.
If absolutely required, overriding the page title text to show in table of contents is possible via toc-title attribute.
If a node does not have its id attribute specified, it will still appear in the table of contents but will be greyed out and not clickable. It acts as a placeholder for a documentation item. A placeholder is useful to keep track of what should be documented, but hasn't yet, and can be helpful to show readers that the topic exists, but isn't yet documented (Pull Requests always welcome!).
When renaming pages, redirects must be configured so existing bookmarks continue working. All existing links in other topics must be updated.
Specify the previous path(s) including .html extension in accepts-web-file-names attribute:
<toc-element
id="themes_getting_started.md"
accepts-web-file-names="themes.html,themes-intro.html"/>This document describes the coding guidelines used for authoring open-source IntelliJ Platform SDK code samples. Before you begin, please read this page thoroughly, as well as the Code of Conduct (Code of Conduct) and License (https://github.com/JetBrains/intellij-sdk-docs/blob/main/LICENSE.txt) documents.
For information about contributing to the IntelliJ Platform itself, visit Contributing to the IntelliJ Platform (Contributing to the IntelliJ Platform).
See also Code Samples for an overview.
Keep the following considerations in mind while authoring an SDK code sample:
The purpose of an SDK sample is to demonstrate an implementation pattern of the IntelliJ Platform.
SDK code samples are instructional code, intended to teach.
Instructional code differs from production code in some key aspects:
Sacrifice some robustness in the interest of simplicity and brevity. Use error checking where it is necessary to make a point about an implementation pitfall.
Keep implementations as simple as possible, but use the full features of the IntelliJ Platform, programming language, and libraries.
Use meaningful names for interfaces, classes, fields, methods, and extension points.
Write instructional Javadoc comments.
Code samples replace lots of documentation.
Aim for two levels of SDK samples:
A basic sample is focused on a particular subject by demonstrating a limited area of the IntelliJ Platform. It should show all the components and architecture but ultimately accomplish something elementary. For example, demonstrate persistence by storing only a String.
An advanced sample demonstrates how different parts of the IntelliJ Platform integrate and work together, such as combining inspections or intentions with non-trivial PsiTree manipulation.
Ultimately, the goal is to provide developers with roadmaps for implementing functionality in an IntelliJ Platform-based plugin. Each roadmap should contain:
Pointers to SDK documentation about the IntelliJ Platform APIs needed to implement the functionality.
Pointers to relevant basic SDK sample plugins.
Pointers to related advanced SDK sample plugins.
Use the standard intellij-community copyright notice in all sample plugins authored by JetBrains:
Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.The copyright statement must appear at the top of every source file. Use the IntelliJ Platform SDK (https://github.com/JetBrains/intellij-sdk-docs/tree/main/.idea/copyright) copyright profile.
For basic samples, the plugin directory name is derived from the IntelliJ Platform extension points demonstrated. For example, foo_basics where foo corresponds to an IntelliJ Platform framework, extension point, or component. Some naming examples include facet_basics and editor_basics. There is only one basic sample per IntelliJ Platform API area.
For advanced code samples, the name should reflect the complex functionality delivered by the plugin rather than the IntelliJ Platform APIs. Advanced samples will be cross-referenced to the IntelliJ Platform APIs demonstrated in the sample.
The only symbol characters allowed in the name of a plugin root directory are underscores, such as foo_basics or max_opened_projects. However, underscores should not be used in any other symbol names, such as Artifact ID, plugin ID, package names, etc. Instead, concatenate a long name into camelCase such as maxOpenedProjects.
When creating a Gradle-based IntelliJ Platform plugin, the plugin's Maven coordinates (Group ID, Artifact ID, Version) are defined.
The Group ID for SDK plugins is always org.intellij.sdk.
The Artifact ID is a brief derivative of the plugin directory name. An Artifact ID should not contain symbols.
For basic code samples, it is not necessary to include "basic" in the Artifact ID. For example, the foo_basics directory name would have the Artifact ID foo.
A plugin with a longer directory name, such as conditional_operator_intention, could have the more succinct Artifact ID of conditionalOperatorIntention. (For legacy reasons, the conditional_operator_intention plugin uses a more concise Artifact ID.)
The plugin ID appears between <id> ("id" in "Plugin Configuration File") elements in the plugin.xml (Plugin Configuration File) file.
In general, the plugin ID is the Group ID concatenated with the Artifact ID. For example, a plugin like facet_basics has the plugin ID org.intellij.sdk.facet.
Plugin IDs do not contain underscores.
Packages in plugins should begin with the plugin ID. If there is only one package in a plugin, then the package name is the same as the plugin ID. Additional suffix words, separated by "." characters, can be added to form more specific package names. Package names do not contain underscores.
SDK sample code should have a standard directory footprint. The standardized structure not only makes the samples simpler to navigate and understand, but it builds on the default Gradle plugin project structure.
Note that directories below the plugin root folder should not have underscore characters, and should use camelCase if needed. The following is an example directory structure for a foo_basics plugin.
code_samples/
foo_basics/
gradle/
src/
main/
java/
org.intellij.sdk.foo/
icons/
SdkIcons.java # The standard SDK icon class
resources/
icons/
sdk_16.svg # The standard SDK icon for menus, etc.
META-INF/
plugin.xml # The plugin configuration file
pluginIcon.svg # The standard SDK plugin icon
test/ # Omit if there are no tests
java/
org.intellij.sdk.foo/
resources/
build.gradle.kts
gradlew
gradle.bat
settings.gradle.kts
README.mdSDK code samples must be developed using Gradle (Creating a Plugin Gradle Project). As of this writing, the use of Gradle in SDK code samples still relies heavily on the plugin.xml for specifying the plugin configuration. At a later, second phase, the SDK code samples will transition to rely more on the Gradle configuration.
The default contents of a Gradle build script file are produced by the New Project Wizard ("Create IDE Plugin" in "Creating a Plugin Gradle Project"). A consistent structure for an SDK code sample's Gradle build script file is essential for clarity and is based on the default produced by the project wizard. Comments in SDK code sample Gradle build scripts should only draw attention to the parts of the Gradle configuration that are unique for a plugin.
For SDK code samples, a few alterations are needed to the default build.gradle.kts file produced by the plugin wizard:
Maintain the Gradle properties version (project.version) and group (project.group). See the Plugin Gradle Properties ("Plugin Gradle Properties and Plugin Configuration File Elements" in "Creating a Plugin Gradle Project") section for how these Gradle properties relate to the elements in plugin.xml.
Add the following statement to the Patching DSL ("patchPluginXml" in "Gradle IntelliJ Plugin") (patchPluginXml {...}) section:
// Patches <version> value in plugin.xml
version.set(project.version)
sinceBuild.set("221")
untilBuild.set("223.*")A consistent structure for the plugin.xml configuration file (Plugin Configuration File) of an SDK code sample is important because we want to draw attention to the unique parts of the file for a plugin. Comment profusely about unique elements and configurations, and comment sparingly for the rest.
The sequence of elements in an SDK code sample plugin.xml file is:
<id> ("id" in "Plugin Configuration File") Use the fully qualified Plugin ID.
<name> ("name" in "Plugin Configuration File") The name value does not have to match the Plugin Directory Name. The name is used for display purposes and should reflect the functionality of the plugin. The name must start with "SDK: ".
<version> ("version" in "Plugin Configuration File") The code sample's version in MAJOR.MINOR.FIX format.
MAJOR corresponds to a significant upgrade in functionality.
MINOR corresponds to minor refactoring and small improvements in functionality.
FIX corresponds to changes that fix problems without significant refactoring.
<depends> ("depends" in "Plugin Configuration File") Include at least one dependency with the module com.intellij.modules.platform to indicate basic plugin compatibility with IntelliJ Platform-based products. Add <depends> elements containing module FQNs as needed to describe more specialized Compatibility with Multiple Products (Plugin Compatibility with IntelliJ Platform Products), and any other Plugin Dependencies (Plugin Dependencies).
<description> ("description" in "Plugin Configuration File") is a concise explanation of what is being demonstrated and how a user would access the functionality. If the plugin by default overrides IDE behavior (such as tree_structure_provider) it must be noted in the description.
<change-notes> ("change-notes" in "Plugin Configuration File") is an ordered list by version numbers with a brief description of changes for each version.
<vendor> ("vendor" in "Plugin Configuration File") Set the value to IntelliJ Platform SDK. Set the attributes:
email omit this attribute.
url to the JetBrains Marketplace "https://plugins.jetbrains.com"
The remainder of the plugin configuration elements (Plugin Configuration File) should only appear if they are needed by a specific plugin.
Each code sample provided within the IntelliJ Platform SDK should contain a README file describing the sample purpose and its content. The SAMPLE_README.md (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/SAMPLE_README.md) file contains a template that should be used as an initial draft for further writing.
Each README.md file is supposed to have the same structure for better navigation and readability:
A header with the link to the main IntelliJ SDK documentation and a page that the sample refers to.
Quickstart section briefly describes the sample's purpose and significant parts of the IntelliJ SDK it implements.
Extension Points section (if implements) with a listing of implemented extension points, links to the implementation classes, and names of extended classes.
Actions section (if implements) with a listing of implemented actions, action IDs, links to the implementation classes, and names of base action classes.
Listeners section (if implements) with a listing of implemented application- or project-level listeners, links to the implementation classes, and names of base listener classes.
Each link that appears in the documentation has to be listed on the very bottom of the file with the clear link ID and proper prefix depending on the link context (docs:, file:, etc.).
IntelliJ Platform SDK code samples should be built and tested against the since-build version.
Code samples should build cleanly, with no warnings or errors, and new code samples should pass all default IntelliJ IDEA code inspections.
Testers should complete the following checklist. Here the term "IDE" means the IntelliJ Platform-based IDE in which the plugin is designed to run:
The plugin should load in the IDE.
The correct information about the plugin should display in the Settings | Plugins panel.
If applicable, the plugin UI, such as tool windows, menu additions, etc. should display correctly.
The functionality of the plugin should be tested with a sample file.
If applicable, the plugin should pass unit tests.
The JetBrains Open Source and Community Code of Conduct.
This project and the corresponding community is governed by the JetBrains Open Source and Community Code of Conduct (https://github.com/jetbrains#code-of-conduct). Please make sure you read it.
This guide comes with a number of sample plugins available from dedicated intellij-sdk-code-samples (https://github.com/JetBrains/intellij-sdk-code-samples) GitHub repository.
Please see README.md which lists all available code samples with a short description.
Each sample is stored in a dedicated folder and is accompanied by its own README.md. Links to the corresponding tutorial or reference page in this tutorial, as well as a list of relevant show-cased elements are provided.
All sample plugins are based on Gradle, see Creating a Plugin Gradle Project to get started.
Additionally, the Working with Gradle in IntelliJ IDEA (https://youtu.be/6V6G3RyxEMk) screencast offers a thorough introduction to Gradle functionality inside IntelliJ IDEA.
Make sure plugins Git, Gradle, and Plugin DevKit are enabled.
Plugin DevKit plugin is bundled with IntelliJ IDEA until 2023.2.
Plugin DevKit AvailabilityWhen using IntelliJ IDEA 2023.3 or later, the Plugin DevKit plugin must be installed from JetBrains Marketplace (Plugin Homepage (https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is no longer bundled with the IDE.
Clone the intellij-sdk-code-samples (https://github.com/JetBrains/intellij-sdk-code-samples) GitHub repository via Git | Clone.... After successful cloning, the IDE suggests opening the project.
Select the code sample(s) to import via the Gradle tool window (https://www.jetbrains.com/help/idea/gradle.html#link_gradle_project).
Alternatively, import all code samples available by choosing _gradleCompositeBuild, which links all Gradle projects in a Composite Build.
After successful import, the project appears in the Gradle tool window tree as a new node.
Assign a Java 17 SDK in Settings | Build, Execution, Deployment | Build Tools | Gradle for Gradle JVM.
Invoke Reload All Gradle Projects from the Gradle tool window toolbar if necessary.
Run the plugin by using the Gradle runIde ("Executing the Plugin" in "Creating a Plugin Gradle Project") task shown under the corresponding project's Tasks node in the Gradle tool window.
This page is also available in Chinese (switch Language in the top bar).
本页面也提供简体中文版本,请在页面右上方切换 Language 为简体中文。
Missing some documentation? Or got stuck developing your plugin? See all the ways of obtaining support below for each case.
Please use English in all communication.
文档不全?或者开发插件的时候被问题卡住了?请阅读下面各种情况下获得支持的所有方式。
请在所有的交流中使用英语。
If you're having problems with the guide itself, such as missing, incorrect, or confusing content, please raise an issue on YouTrack (https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=). If the problem is easily solved, you can also submit a Pull Request (Contributing to the IntelliJ Platform SDK).
If you want to report (smaller) issues for a specific page, you can also use Was this page helpful feedback form on the bottom of each page by providing more details after clicking the No button. Please try to be as specific as possible and provide your e-mail address for further questions and getting notified of updates.
If you just want to share feedback on the guide, again, raise an issue (https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=), even if it's for a discussion, ideas on improvements or suggestions.
Please don't use the IJSDK YouTrack project for plugin or product support requests - it's intended for the SDK and the documentation, and you'll get a better response using one of the methods below.
如果你在使用本指南的时候遇到问题,例如内容缺失,错误或者内容混乱,请在 YouTrack上提交问题 (https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=) 。如果问题很容易得到解决,你也可以提交 拉取请求 (Contributing to the IntelliJ Platform SDK)。
如果你想报告特定页面的(小)问题,你还可以使用每个页面底部的 Was this page helpful 反馈表,方法是点击No按钮后提供更多详细信息。请尽量具体的描述您的问题并提供您的电子邮件地址,以便于后续问题沟通并在更新时获取通知。
如果你只是想对本指南提供反馈,同样也是通过提交问题 (https://youtrack.jetbrains.com/newIssue?project=IJSDK&clearDraft=true&c=)来进行,即便只是为了讨论,提出改进的想法或建议。
请不要使用 IJSDK YouTrack 项目来发起插件开发或者产品支持请求 — 这个项目的本意是关于SDK和文档的。如果使用下列方式之一,你会更好的得到响应。
Please see Plugin Development FAQ (Plugin Development FAQ) and Explore the IntelliJ Platform API (Explore the IntelliJ Platform API) for tips & tricks.
For questions and problems related to plugin development itself (rather than the content of this guide), you have several options:
#intellij-platform on JetBrains Platform Slack (https://plugins.jetbrains.com/slack/) - this chat room is a great place to ask questions about extending JetBrains products, with answers coming from both JetBrains team members and members of the community
Plugin Development Forum (https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development) - post a message to the support forum. It is regularly updated with answers by JetBrains team members.
Of course, all raised questions will be used to try and improve this guide.
请参考 插件开发常见问题问答 (Plugin Development FAQ) 和 探索 IntelliJ Platform API (Explore the IntelliJ Platform API) 来获取相关技巧和窍门。 对于与插件开发本身有关的问题和难题(和本指南内容无关),你有几个选择: JetBrains Platform Slack上的#intellij-platform — 这个聊天室是讨论有关扩展JetBrains产品问题的好地方,解答来自JetBrains团队成员和社区成员。
插件开发论坛 (https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development) - 在支持论坛上发布消息。JetBrains团队成员会定期更新并解答问题。 当然,所有收到的问题都将用于尝试和改进本指南。
If you have a problem with an IntelliJ Platform-based product, such as IntelliJ IDEA, WebStorm, Rider, etc., please use any of the regular Product Support (https://www.jetbrains.com/support/) channels.
如果在使用基于 IntelliJ 平台的产品(例如 IntelliJ IDEA、WebStorm、Rider 等)时遇到问题,请使用任何常规产品支持 (https://www.jetbrains.com/support/)渠道。
This page lists notable additions and updates to the SDK documentation and Code Samples.
See GitHub Changelog (https://github.com/JetBrains/intellij-sdk-docs/commits/main) (RSS (https://github.com/JetBrains/intellij-sdk-docs/commits/main.atom)) for a detailed changelog.
Staying up to dateSubscribe to Marketplace Developer News (https://jb.gg/mp-updates) to receive news and announcements. Also follow JBPlatform (https://x.com/JBPlatform/) on X (formerly Twitter) and visit JetBrains Platform Blog (https://blog.jetbrains.com/platform/) and JetBrains Marketplace on LinkedIn (https://www.linkedin.com/showcase/jetbrains-marketplace/).
Add Internationalization and Providing Translations pages describing IDE and plugin translation possibilities and best practices.
How to mark functionality available during indexing via "DumbAware API" in "Indexing and PSI Stubs".
Move Extension Point and Listener Lists to the Resources section and split the main Extension Point and Listener List into: IntelliJ Platform, IntelliJ Community Plugins, and Android Plugin.
Add Kotlin Coroutines describing how to write asynchronous code in an imperative style.
Add documentation ("Error Handling" in "Extension Points") on how to handle errors and deprecations in extensions.
Note changes in how highlighting is now performed more efficiently ("Order of Running Highlighting" in "Syntax and Error Highlighting") in 2024.1.
Add documentation (IntelliJ Platform Gradle Plugin 2.x (Beta)) for the next generation of Gradle tooling for plugin development.
Revamp JCEF (Java Chromium Embedded Framework) (Embedded Browser (JCEF)) page.
Added User Interface FAQ.
Add information about executing actions programmatically ("Executing Actions Programmatically" in "Actions").
Please see "Attaching Sources" in "Gradle IntelliJ Plugin" on how to set up 2023.2/3 IDEs for Gradle plugin projects.
Add an example ("Implementing the PersistentStateComponent Interface" in "Persisting State of Components") of a persistent state component implemented in Kotlin.
Add section "Storing a reference to a Module" in "Module".
Add "Macros" in "Run Configurations" section describing how to support dynamic expandable values in run configuration settings.
Update Inlay Hints page with the information about new APIs.
Update General Threading Rules to reflect changes in 2023.3 platform.
Add information about ordering quick fixes ("Quick Fix Implementation" in "Code Inspections") and intentions actions ("Techniques Used" in "Intentions").
Clarify the information about declarative inlay hints ("Declarative Inlay Hints Provider" in "Inlay Hints") customization possibilities.
Describe techniques for simplifying run configuration settings editors ("Simplifying Settings Editors" in "Run Configurations").
Rework Execution, Run Configurations, and Run Configurations Tutorial pages.
Clarify the syntax highlighting ("Syntax Highlighting" in "Testing Highlighting") test file format and test implementation initial approach.
Clarify referencing icons by paths and icon holder class constants in Working with Icons.
Add information about requirements for persistent state components to be included in the Settings Sync plugin synchronization mechanism ("Settings Sync Plugin" in "Persisting State of Components").
Rework Documentation page and adapt it to the new DocumentationTargetProvider framework.
Add Web Symbols documentation, which is a framework that simplifies web technology development by utilizing the Symbols API and supporting custom syntaxes.
Added Open Source Plugins Extension Point and Listener List for plugins bundled with IntelliJ IDEA Ultimate and other IDEs.
Add a section on Inspection Options, which allows extending inspection behavior based on the input provided by user at runtime.
Add section on "New UI Icons" in "Working with Icons".
Document "RuntimeDictionaryProvider" in "Spell Checking" EP for spellchecking.
Update IDE Support section in Verifying Plugin Compatibility.
UI Inspector: update "Specific Component Properties" in "Internal Actions - UI Inspector" and add section "Inspecting Settings" in "Internal Actions - UI Inspector".
Add information about how to prepare intentions to show Intention Action Preview.
Add information for new "Cell.align" in "Kotlin UI DSL Version 2" methods in Kotlin UI DSL Version 2.
Add a new section about how to improve plugin UX (Plugin User Experience (UX)) and overall plugin quality.
Add information about threading in Actions in "Principal Implementation Overrides" in "Actions".
Add information about sharing settings ("Sharing Settings Between IDE Installations" in "Persisting State of Components") between different IDEs installations.
All the content related to themes customization (Theme Structure) and creating a project using the DevKit approach (Developing a Theme) has been moved to a new Themes (Getting Started) part. Content has been refreshed to match the current state of the project and SDK wizards.
Add Spell Checking section with an accompanying tutorial (21. Spell Checking) showing how to implement a spell checking for a custom language.
Add descriptions for the following EPs to Additional Minor Features: "Prevent Error Highlighting of Files" in "Additional Minor Features", "Provide Fully Qualified Names (FQN) for Elements" in "Additional Minor Features", "Label Files as Test Files" in "Additional Minor Features", "Move Statements Up and Down in the Editor" in "Additional Minor Features".
Add section about "Power Save Mode" in "IDE Infrastructure".
Highlight references automatically via "Additional Highlighting" in "References and Resolve"
Language injections: controlling "Formatting" in "Language Injection"
Update the Plugin Configuration File page to describe all the elements in detail.
All source links now point to GitHub instead of Upsource (which is going to be sunset (https://blog.jetbrains.com/upsource/2022/01/31/upsource-end-of-sales-announcement/)).
Add section Status Bar Widgets describing how to implement your own status bar widgets.
Add overview of "Useful Action Base Classes" in "Actions".
Add section PHP Type Providers about type providers describing type inference in PhpStorm and how to implement your own type provider.
Add Postfix Completion section explaining how to implement generating or wrapping the existing code into additional constructs without navigating the caret back.
Add Gradle IntelliJ Plugin documentation to Tooling.
Add the Bundling Plugin API Sources section explaining how to expose plugin API sources to dependent plugin developers.
Add a small section to "Utility Classes" in "PHP Open API" describing PhpFilePathUtils utility class.
Add mention of the way to programmatically open an autocomplete popup to Code Completion.
Add a small section to "Animated Icons" in "Working with Icons" describing animated icons.
Moved Gradle Grammar-Kit Plugin documentation to Tooling.
Add Navigation Bar section with an accompanying tutorial (15. Structure Aware Navigation Bar) showing how to implement a navigation bar for a custom language.
Add section Inlay Hints describing special markers that appear in the editor and provide additional information, like the names of the parameters that a called method expects.
Add a small section to Editor Components describing convenient EditorTextField subclasses.
Add descriptions for the following EPs to Additional Minor Features: "Recognizing Complex Multi-Block Expressions" in "Additional Minor Features", "Breadcrumbs" in "Additional Minor Features", "Plain Text Completion" in "Additional Minor Features", "Splitting and Joining List Constructs" in "Additional Minor Features", "Suggesting Rename and Change Signature Refactorings" in "Additional Minor Features", "Reader Mode" in "Additional Minor Features", "Background Colors for Editors and Project View" in "Additional Minor Features", "Custom Names and Tooltips for Editor Tabs" in "Additional Minor Features".
Add small section to "Decorating Project View Nodes" in "Project View" describing how to modify the representation of nodes in the project view.
Add Rename Refactoring paragraphs mentioning RenameInputValidator(Ex).
As API annotated with ApiStatus.@Internal must not be used in plugins, refer to Internal API Migration for a list corresponding API replacements and additional information.
All Code Samples now use Kotlin DSL in their Gradle build scripts.
Add Android Studio Releases List section containing a complete list of the Android Studio releases with the relevant IntelliJ IDEA release version specified.
Add Alternatives to Implementing a Plugin section describing alternative approaches to extending IDE functionality without actual plugin development.
Add Parameter Info section explaining how information about function parameters can be shown in the editor.
Add File and Code Templates section explaining how to implement functionality that allows generating files and code fragments containing repetitive text and patterns.
Add Syntax Errors and Controlling Highlighting sections explaining syntax highlighting basics and filtering highlighting information.
Add sections "Application Events" in "IDE Infrastructure" and "Plugin Management" in "IDE Infrastructure".
Potentially unsafe features must be guarded using Trusted Project API (Trusted Project).
Add Language Injection section that shows how the IntelliJ Platform handles different languages within the same source file.
IDE Infrastructure handles Logging, Error Reporting, Runtime Information, and how to provide Context Help.
IntelliJ Platform Extension Point and Listener List now contains sections listing all provided Listener (Listeners) Topics. See also corresponding Extension Point Lists under Product Specific. Also, all deprecated API now has a dedicated tag.
Plugin Signing describes the plugin signing process, explains how to generate a certificate, configure the Gradle signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task, and introduces a standalone CLI tool.
Testing FAQ page lists common issues, useful classes, and techniques for writing and maintaining tests.
Add Documentation section with an accompanying tutorial (20. Documentation) that show how to implement a DocumentationProvider for custom languages.
See Product Specific.
Add a new section Explore the IntelliJ Platform API that describes how plugin authors work with the IntelliJ Platform API and what tools they use.
Add a new section about Element Patterns (Element Patterns) that are used when implementing Completion Contributors (9. Completion Contributor) or PSI Reference Contributors ("Contributed References" in "PSI References").
Add a new section about Text Selection EPs (Text Selection) and describe ExtendWordSelectionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/ExtendWordSelectionHandler.java).
Added a code sample to the SDK tutorial that expands on assisting in the setup of an SDK ("Assisting in Setting Up an SDK" in "SDK").
Unified Abstract Syntax Tree (UAST) (UAST - Unified Abstract Syntax Tree) allows providing features that will work across all supported JVM languages (Java, Kotlin, Scala, Groovy).
Explore usages of Extension Points (IntelliJ Platform Extension Point and Listener List) in open-source plugins using IntelliJ Platform Explorer (https://jb.gg/ipe).
All EPs available in IJ Platform and Android (IntelliJ Platform Extension Point and Listener List) can now be browsed conveniently.
All code samples used in this guide now come with README, making it easier to browse them. They can be conveniently accessed via a separate GitHub repository (https://github.com/JetBrains/intellij-sdk-code-samples).
Added new sections Code and Troubleshooting to Dynamic Plugins (Dynamic Plugins).
Create new plugins with a preconfigured project scaffold and CI in one click (IntelliJ Platform Plugin Template).
Added reference (Disposer and Disposable) discussing resource cleanup/management.
Added guide (Settings Guide) and tutorial (Settings Tutorial) on integrating with IDE Settings dialog.
Inspect Swing components and associated data (like AnAction for menu item) using UI Inspector (Internal Actions - UI Inspector).
Allows embedding (Embedded Browser (JCEF)) Chromium-based browser in the IDE.
All samples (https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples) now use the recommended solution (Creating a Plugin Gradle Project) of setting up plugin projects.
The corresponding tutorial (Custom Language Support Tutorial) and Testing a Custom Language Plugin (Testing a Custom Language Plugin) have been updated and enhanced as well.
Product Specific (Plugin Compatibility with IntelliJ Platform Products) has been expanded massively, now also covering each IDE with its dedicated page.
Added starting point Dynamic Plugins (Dynamic Plugins) for migrating plugins (IntelliJ Platform 2020.1 and later).
Components being a legacy feature, the updated page (Components) describes migrating them to modern replacement API.
All contents have been moved to JetBrains Marketplace Documentation (https://plugins.jetbrains.com/docs/marketplace/).
Optimizing performance when working with PSI (PSI Performance), during indexing ("Improving Indexing Performance" in "Indexing and PSI Stubs"), and "Avoiding UI Freezes" in "General Threading Rules".
Describes preferred way (Kotlin UI DSL Version 1) of building UI/dialogs for IntelliJ Platform 2019.2 and later.
This section covers the basics of working with the IntelliJ Platform. It will familiarize you with the working environment, project structure, and frequently used API components.
Implementing Plugin in Kotlin (Configuring Kotlin Support)
If you are interested in the UI theme development, please see Themes (Getting Started).
In some cases, implementing an actual IntelliJ Platform plugin can be overkill, and using one of the alternative approaches listed below may provide you with the required value in a much shorter time. If you need a functionality that is specific to your project domain, conventions, or practices, you can avoid all the steps that are required to implement and publish a plugin and provide these features as a part of your project or IDE configuration files.
Before you start the IntelliJ Platform plugin development, define your requirements and verify if they can be covered with any of the alternatives described below. Consider implementing an actual plugin only when the described solutions are insufficient in your case and there is a significant number of developers who can benefit from it.
The Structural Search and Replace (SSR) (https://www.jetbrains.com/help/idea/structural-search-and-replace.html) functionality allows defining search patterns which are based not only on textual information but also on the structure of the searched code fragments, no matter how it is formatted or commented. The SSR templates can be used for creating custom inspections (https://www.jetbrains.com/help/idea/creating-custom-inspections.html), which can be an alternative for programmatic code inspections (Code Inspections). Depending on requirements, an inspection can report an issue for a code fragment matching a given template, but also provide a quick fix replacing the reported fragment with the configured replacement template. All inspection metadata like name, problem tooltip, and description are configurable. A single inspection can use multiple search and replacement templates.
Once SSR inspections are created and configured, they can be shared with other team members via inspection profiles (https://www.jetbrains.com/help/idea/customizing-profiles.html).
SSR inspections can be created only for languages providing SSR support. To verify if a given language supports SSR, invoke the Edit | Find | Search Structurally... action in an IDE supporting the language, and check if it is present in the Language select list.
See the I(J)nspector (https://ijnspector.wordpress.com/) blog for practical SSR templates examples.
The IDE scripting console (https://www.jetbrains.com/help/idea/ide-scripting-console.html) can be used for automating IDE's features and extracting required information, e.g., about a current project. Scripts can access the IntelliJ Platform APIs and can be implemented in Kotlin, JavaScript, or Groovy by default, but it is also possible to use other languages compliant with the JCR-223 specification (https://www.jcp.org/en/jsr/detail?id=223).
Created scripts are stored in the IDE configuration directory (https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory) and can't be shared as part of project files or configuration.
The Flora (https://plugins.jetbrains.com/plugin/17669-flora-beta-) plugin allows for developing project-specific extensions as Kotlin Script (*.kts) or JavaScript (*.js) files. Flora extensions have access to all available IntelliJ Platform APIs, just like a regular plugin.
Every extension is represented by a single file and stored directly in a project's .plugins directory. Extensions can be easily shared with other team members by adding the .plugins directory to VCS. Also, adding the Flora plugin in the Settings | Build, Execution, Deployment | Required Plugins and sharing this configuration as part of a project makes it effortless to deliver additional IDE functionalities to your team without any manual setup.
Please note that the Flora plugin is in an experimental state.
The LivePlugin (https://plugins.jetbrains.com/plugin/7282-liveplugin) allows for extending IntelliJ-based IDEs functionalities at the runtime, without the need of restarting IDE. It adds a new Plugins tool window that lists all available extensions and allows managing them. Extensions can be implemented in Kotlin or Groovy and edited directly in the IDE. Extensions can use all IntelliJ Platform APIs and additional LivePlugin API that shorten common use cases.
Created extensions are stored on the IDE level and can be shared with other team members as plain files, GitHub gists, or repositories. Additionally, if they are stored in a project's .live-plugins directory and LivePlugin's Run Project Specific Plugins option is enabled, all extensions from this directory will be loaded automatically when the project is opened and unloaded when the project is closed.
See the LivePlugin description (https://dmitrykandalov.com/liveplugin), presentation (https://www.youtube.com/watch?v=GcYa4lMRta0), and extensions examples (https://github.com/dkandalov/live-plugin#more-examples) for more information.
PhpStorm (https://www.jetbrains.com/phpstorm/) supports special metadata files (https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html) describing the behavior of methods and functions. This information is used for using the existing IDE features such as code completion, navigation, finding usages, and others. The metadata files can be part of project files (https://www.jetbrains.com/help/phpstorm/ide-advanced-metadata.html#create-metadata-files-inside-your-project), which makes it easy to share it between team members via version control.
The IntelliJ Platform is a JVM application, implemented mostly in Java and Kotlin (https://kotlinlang.org). At this time, it's not possible to develop plugins for the IntelliJ Platform in non-JVM languages.
Developing a plugin for the IntelliJ Platform requires knowledge and experience with the following technologies and concepts:
Java, Kotlin, or other JVM language, and its standard and 3rd-party libraries
Gradle (https://gradle.org/) or a similar build system (e.g., Maven)
Swing (https://en.wikipedia.org/wiki/Swing_(Java)) for building user interfaces
Java Concurrency Model (https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html)
experience with IntelliJ Platform-based IDE (e.g., IntelliJ IDEA (https://www.jetbrains.com/idea/))
Please keep in mind that the IntelliJ Platform is a large project, and while we are doing our best to cover as many topics as possible, it is not possible to include every feature and use-case in the documentation. Developing a plugin will sometimes require digging into the IntelliJ Platform code (https://github.com/JetBrains/intellij-community) and analyzing the example implementations in other plugins (https://jb.gg/ipe).
It's highly recommended to get familiar with the Explore the IntelliJ Platform API section before you start the plugin implementation.
Plugin AlternativesIn some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as alternative solutions (Alternatives to Implementing a Plugin) exist.
Products based on the IntelliJ Platform can be modified and adjusted for custom purposes by adding plugins. All downloadable plugins are available from the JetBrains Marketplace (https://plugins.jetbrains.com/).
The most common types of plugins include:
Custom language support
Framework integration
Tool integration
User interface add-ons
Themes
Plugin AlternativesIn some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as alternative solutions (Alternatives to Implementing a Plugin) exist.
Custom language support provides basic functionality for working with a particular programming language, that includes:
File type recognition
Lexical analysis
Syntax highlighting
Formatting
Code insight and code completion
Inspections and quick fixes
Intention actions
Plugins can also augment existing (bundled) custom languages, e.g., by providing additional inspections, intentions, or any other features.
Refer to the Custom Language Support Tutorial (Custom Language Support Tutorial) to learn more about the topic.
Framework integration consists of improved code insight features, which are typical for a given framework, as well as the option to use framework-specific functionality directly from the IDE. Sometimes it also includes language support elements for a custom syntax or DSL.
Specific code insight
Direct access to framework-specific functionality
Refer to the IntelliJ-HCL (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/terraform) as an example of framework integration. More reference plugins can be found on JetBrains Marketplace (https://plugins.jetbrains.com/search?orderBy=update%20date&shouldHaveSource=true&tags=Framework).
Tool integration makes it possible to manipulate third-party tools and components directly from the IDE without switching contexts, that implies:
Implementation of additional actions
Related UI components
Access to external resources
Refer to the Gerrit integration (https://plugins.jetbrains.com/plugin/7272) plugin as an example.
Plugins in this category apply various changes to the standard user interface of the IDE. Some newly added components are interactive and provide new functionality, while others are limited to visual modifications only. The Foldable ProjectView (https://plugins.jetbrains.com/plugin/17288-foldable-projectview) plugin may serve as an example.
Themes (Getting Started) give designers the ability to customize the appearance of built-in IDE UI elements.
IntelliJ Platform plugins can be developed by using either IntelliJ IDEA Community Edition (https://www.jetbrains.com/idea/download/) or IntelliJ IDEA Ultimate (https://www.jetbrains.com/idea/download/) as your IDE. It is highly recommended to always use the latest available version, as the plugin development tooling support from Plugin DevKit continues supporting new features.
Before starting with the actual development, make sure to understand all requirements to achieve best Plugin User Experience (UX).
Plugin AlternativesIn some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as alternative solutions (Alternatives to Implementing a Plugin) exist.
The recommended solution for building IntelliJ Platform plugins is Gradle IntelliJ Plugin.
The IntelliJ IDEA Ultimate and Community editions provide the necessary plugins to support Gradle-based plugin development: Gradle and Plugin DevKit. To verify these plugins are installed and enabled, see the help section about Managing Plugins (https://www.jetbrains.com/help/idea/managing-plugins.html).
Plugin DevKit plugin is bundled with IntelliJ IDEA until 2023.2.
Plugin DevKit AvailabilityWhen using IntelliJ IDEA 2023.3 or later, the Plugin DevKit plugin must be installed from JetBrains Marketplace (Plugin Homepage (https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is no longer bundled with the IDE.
Gradle IntelliJ Plugin manages the dependencies of a plugin project - both the base IDE and other plugin dependencies (Plugin Dependencies). It provides tasks to run the IDE with your plugin and to package and publish ("Publishing Plugin With Gradle" in "Publishing a Plugin") your plugin to the JetBrains Marketplace (https://plugins.jetbrains.com). To make sure that a plugin is not affected by API changes (Incompatible Changes in IntelliJ Platform and Plugins API), which may happen between major releases of the platform, you can quickly verify your plugin against other IDEs and releases.
There are two main ways of creating a new Gradle-based IntelliJ Platform plugin project:
dedicated generator available in the New Project Wizard (https://www.jetbrains.com/help/idea/new-project-wizard.html) - it creates a minimal plugin project with all the required files
IntelliJ Platform Plugin Template available on GitHub - in addition to the required project files, it includes configuration of the GitHub Actions CI workflows
This documentation section describes plugin structure generated with the New Project wizard, but the project generated with IntelliJ Platform Plugin Template covers all the described files and directories. See the IntelliJ Platform Plugin Template section for more information about the advantages of this approach and instructions on how to use it.
The old DevKit project model and workflow are still supported in existing projects and are recommended for creating theme plugins (Developing a Theme). See how to migrate a DevKit plugin to Gradle (Migrating DevKit Plugin to Gradle).
A dedicated SBT plugin (https://github.com/JetBrains/sbt-idea-plugin) is available for plugins implemented in Scala.
Creating a Gradle-based Plugin Project (Creating a Plugin Gradle Project)
Configuring the Gradle IntelliJ Plugin (Configuring Gradle IntelliJ Plugin)
Adding Kotlin Support (Configuring Kotlin Support) (optional)
Publishing a Plugin (Publishing a Plugin)
This documentation page describes a Gradle-based plugin project generated with the New Project Wizard (https://www.jetbrains.com/help/idea/new-project-wizard.html), but the project generated with IntelliJ Platform Plugin Template covers all the described files and directories.
Plugin DevKit plugin is bundled with IntelliJ IDEA until 2023.2.
Plugin DevKit AvailabilityWhen using IntelliJ IDEA 2023.3 or later, the Plugin DevKit plugin must be installed from JetBrains Marketplace (Plugin Homepage (https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is no longer bundled with the IDE.
Launch the New Project wizard via the File | New | Project... action and provide the following information:
Select the IDE Plugin generator type from the list on the left.
Specify the project Name and Location.
Choose the Plugin option in the project Type.
Only in IntelliJ IDEA older than 2023.1:
Choose the Language the plugin will use for implementation. For this example select the Kotlin option. See also Kotlin for Plugin Developers (Configuring Kotlin Support) for more information.
Projects generated with IntelliJ IDEA 2023.1 or newer, support both Kotlin and Java sources out of the box. Project generator automatically creates $PLUGIN_DIR$/src/main/kotlin sources directory. To add Java sources, create $PLUGIN_DIR$/src/main/java directory.
Provide the Group which is typically an inverted company domain (e.g. com.example.mycompany). It is used for the Gradle property project.group value in the project's Gradle build script.
Provide the Artifact which is the default name of the build project artifact (without a version). It is also used for the Gradle property rootProject.name value in the project's settings.gradle.kts file. For this example, enter my_plugin.
Select JDK 11. This JDK will be the default JRE used to run Gradle, and the JDK version used to compile the plugin sources.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
After providing all the information, click the Create button to generate the project.
For the example my_plugin created with the steps describes above, the IDE Plugin generator creates the following directory content:
The default IntelliJ Platform build.gradle.kts file (see next paragraph).
The gradle.properties file, containing properties used by Gradle build script.
The settings.gradle.kts file, containing a definition of the rootProject.name and required repositories.
The Gradle Wrapper files, and in particular the gradle-wrapper.properties file, which specifies the version of Gradle to be used to build the plugin. If needed, the IntelliJ IDEA Gradle plugin downloads the version of Gradle specified in this file.
The META-INF directory under the default main source set (https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout) contains the plugin configuration file (Plugin Configuration File) and plugin logo (Plugin Logo).
The Run Plugin run configuration (https://www.jetbrains.com/help/idea/run-debug-configuration.html).
The generated my_plugin project build.gradle.kts file:
plugins {
id("java")
id("org.jetbrains.kotlin.jvm") version "1.9.21"
id("org.jetbrains.intellij") version "1.17.3"
}
group = "com.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
// Configure Gradle IntelliJ Plugin
// Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
intellij {
version.set("2022.2.5")
type.set("IC") // Target IDE Platform
plugins.set(listOf(/* Plugin Dependencies */))
}
tasks {
// Set the JVM compatibility versions
withType<JavaCompile> {
sourceCompatibility = "17"
targetCompatibility = "17"
}
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "17"
}
patchPluginXml {
sinceBuild.set("222")
untilBuild.set("232.*")
}
signPlugin {
certificateChain.set(System.getenv("CERTIFICATE_CHAIN"))
privateKey.set(System.getenv("PRIVATE_KEY"))
password.set(System.getenv("PRIVATE_KEY_PASSWORD"))
}
publishPlugin {
token.set(System.getenv("PUBLISH_TOKEN"))
}
}Three Gradle plugins are explicitly declared:
The Gradle Java (https://docs.gradle.org/current/userguide/java_plugin.html) plugin (java).
The Kotlin Gradle (https://kotlinlang.org/docs/gradle-configure-project.html#apply-the-plugin) plugin (org.jetbrains.kotlin.jvm).
The Gradle IntelliJ Plugin (org.jetbrains.intellij).
The Group from the New Project wizard is the project.group value.
The sourceCompatibility line is injected to enforce using Java 17 JDK to compile Java sources.
The values of the intellij.version ("version" in "Gradle IntelliJ Plugin") and intellij.type ("type" in "Gradle IntelliJ Plugin") properties specify the version and type of the IntelliJ Platform to be used to build the plugin.
The empty placeholder list for plugin dependencies ("plugins" in "Gradle IntelliJ Plugin").
The values of the patchPluginXml.sinceBuild ("sinceBuild" in "Gradle IntelliJ Plugin") and patchPluginXml.untilBuild ("untilBuild" in "Gradle IntelliJ Plugin") properties specifying the minimum and maximum versions of the IDE build the plugin is compatible with.
The initial signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") and publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") tasks configuration. See the "Publishing Plugin With Gradle" in "Publishing a Plugin" section for more information.
Consider using the IntelliJ Platform Plugin Template (https://github.com/JetBrains/intellij-platform-plugin-template) which additionally provides CI setup covered with GitHub Actions.
The Gradle properties rootProject.name and project.group will not, in general, match the respective plugin configuration file (Plugin Configuration File) plugin.xml elements <name> ("name" in "Plugin Configuration File") and <id> ("id" in "Plugin Configuration File"). There is no IntelliJ Platform-related reason they should as they serve different functions.
The <name> element (used as the plugin's display name) is often the same as rootProject.name, but it can be more explanatory.
The <id> value must be a unique identifier over all plugins, typically a concatenation of the specified Group and Artifact. Please note that it is impossible to change the <id> of a published plugin without losing automatic updates for existing installations.
Gradle projects are run from the IDE's Gradle Tool window.
Before running my_plugin, some code can be added to provide simple functionality. See the Creating Actions (Creating Actions) tutorial for step-by-step instructions for adding a menu action.
The IDE Plugin generator automatically creates the Run Plugin run configuration that can be executed via the Run | Run... action or can be found in the Gradle tool window under the Run Configurations node.
To execute the Gradle runIde task directly, open the Gradle tool window and search for the runIde task under the Tasks node. If it's not on the list, hit the re-import button in the toolbar (https://www.jetbrains.com/help/idea/jetgradle-tool-window.html#gradle_toolbar) at the top of the Gradle tool window. When the runIde task is visible, double-click it to execute.
To debug your plugin in a standalone IDE instance, please see How to Debug Your Own IntelliJ IDEA Instance (https://medium.com/agorapulse-stories/how-to-debug-your-own-intellij-idea-instance-7d7df185a48d) blog post.
For more information about how to work with Gradle-based projects see the Working with Gradle in IntelliJ IDEA (https://www.youtube.com/watch?v=6V6G3RyxEMk) screencast and working with Gradle tasks (https://www.jetbrains.com/help/idea/work-with-gradle-tasks.html) in the IntelliJ IDEA help.
The IntelliJ Platform Plugin Template is the alternative solution for creating a new Gradle-based IntelliJ Platform plugin with the New Project Wizard (Creating a Plugin Gradle Project).
IntelliJ Platform Plugin Template (https://github.com/JetBrains/intellij-platform-plugin-template) is a GitHub repository that provides a pure boilerplate template to make it easier to create a new Gradle-based plugin project.
The main goal of this template is to speed up the setup phase of plugin development for both new and experienced developers by preconfiguring the project scaffold and CI, linking to the proper documentation pages, and keeping everything organized.
GitHub Template allows you to create a new repository from the scaffold without having to copy and paste content, clone repositories, or clear the history manually. All you have to do is click the Use this template button on the GitHub project page (you must be logged in with your GitHub account). After that, the GitHub Actions workflow will be triggered to override or remove any template-specific configurations, such as the plugin name, current changelog, etc.
Once this is complete, the project is ready to be cloned to your local environment and opened with IntelliJ IDEA (https://www.jetbrains.com/idea/download).
For more details, please refer to the IntelliJ Platform Plugin Template (https://github.com/JetBrains/intellij-platform-plugin-template) project documentation.
The recording of the Busy Plugin Developer. Episode 0 webinar describes and shows how to use the IntelliJ Platform Plugin Template (https://youtu.be/-6D5-xEaYig?t=230) in detail.
See Revamping Plugins #3 – Migrating from DevKit to the Gradle build system (https://blog.jetbrains.com/platform/2021/12/migrating-from-devkit-to-the-gradle-build-system/) blog post for a step-by-step walk-through.
Converting a plugin created with the old DevKit approach (which can be used for creating themes (Creating a Theme Project)) to a Gradle-based plugin project can be done using the New Project wizard to create a Gradle-based project around the existing DevKit-based project:
Ensure the directory containing the DevKit-based IntelliJ Platform plugin project can be fully recovered if necessary.
Delete all the artifacts of the DevKit-based project:
.idea directory
[modulename].iml file
out directory
Arrange the existing source files within the project directory in the Gradle source set (https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout) format.
Use the New Project ("Create IDE Plugin" in "Creating a Plugin Gradle Project") wizard as though creating a new Gradle-based plugin project from scratch. On the New Project page choose the IDE Plugin generator and set the values of:
Group to the existing package in the initial source set.
Artifact to the name of the existing plugin.
Name to the name of the directory where the existing plugin is located, e.g. if the plugin project base directory is /Users/john/Projects/old_plugin, it should be the old_plugin.
Location to the name of the plugin's parent directory, e.g. if the plugin project base directory is /Users/john/Projects/old_plugin, it should be the /Users/john/Projects.
Click Finish to create the new Gradle-based plugin.
Add more modules (https://www.jetbrains.com/help/idea/gradle.html#gradle_add_module) using Gradle source sets (https://www.jetbrains.com/help/idea/gradle.html#gradle_source_sets) as needed.
This section presents a guided tour of Gradle plugin attributes to achieve the commonly desired functionality. For more advanced options, see the full Gradle IntelliJ Plugin (Gradle IntelliJ Plugin) reference.
Gradle IntelliJ Plugin and Gradle (https://gradle.org/install/) build system are constantly developed, and every new release brings important bug fixes, new features, and improvements that makes the development more efficient. It is strongly recommended to keep updating both Gradle and Gradle IntelliJ Plugin to the latest versions. Newer IDE releases might not be supported fully in older releases of Gradle IntelliJ Plugin.
Current Gradle IntelliJ Plugin version is 1.17.3
Which versions should your plugin support? We've collected some insights based on download statistics in Statistics: Product Versions in Use (https://plugins.jetbrains.com/docs/marketplace/product-versions-in-use-statistics.html).
By default, the Gradle plugin will build a plugin project against the IntelliJ Platform defined by the latest EAP snapshot of the IntelliJ IDEA Community Edition.
If a matching version of the specified IntelliJ Platform is not available on the local machine, the Gradle plugin downloads the correct version and type. IntelliJ IDEA then indexes the build and any associated source code and JetBrains Java Runtime.
To build a plugin for more than one target platform version, see "Targeting Multiple IDE Versions" in "Build Number Ranges" for important notes.
Explicitly setting the intellij.version ("version" in "Gradle IntelliJ Plugin") and intellij.type ("type" in "Gradle IntelliJ Plugin") properties tells the Gradle plugin to use that configuration of the IntelliJ Platform to create the plugin project.
See the Developing for Multiple Products (Plugins Targeting IntelliJ Platform-Based IDEs) page for information about how to develop a plugin that is compatible with multiple IntelliJ-based IDEs.
All available platform versions can be browsed in the IntelliJ Platform Artifacts Repositories.
If the chosen platform version is not available in the repositories, or a local installation of the target IDE is the desired type and version of the IntelliJ Platform, use intellij.localPath ("localPath" in "Gradle IntelliJ Plugin") to point to that installation. If the intellij.localPath attribute is set, do not set the intellij.version and intellij.type attributes as this could result in undefined behavior.
IntelliJ Platform plugin projects may depend on either bundled or third-party plugins. In that case, a project should build against a version of those plugins that match the IntelliJ Platform version used to build the plugin project. The Gradle plugin will fetch any plugins in the list defined by intellij.plugins ("plugins" in "Gradle IntelliJ Plugin"). See the Gradle plugin IntelliJ Extension ("IntelliJ Extension" in "Gradle IntelliJ Plugin") for information about specifying the plugin and version.
Note that this attribute describes a dependency so that the Gradle plugin can fetch the required artifacts. The runtime dependency must be added in the Plugin Configuration (Plugin Configuration File) (plugin.xml) file as described in Plugin Dependencies ("3. Dependency Declaration in plugin.xml" in "Plugin Dependencies").
By default, the Gradle plugin will use the same version of the IntelliJ Platform for the IDE Development Instance as was used for building the plugin. Using the corresponding JetBrains Runtime is also the default, so for this use-case no further configuration is required.
The IntelliJ Platform IDE used for the Development Instance (IDE Development Instance) can be different from that used to build the plugin project. Setting the runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") property will define an IDE to be used for the Development Instance. This attribute is commonly used when running or debugging a plugin in an alternate IntelliJ Platform-based IDE ("IDEs Based on the IntelliJ Platform" in "The IntelliJ Platform").
Every version of the IntelliJ Platform has a corresponding version of the JetBrains Runtime ("Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance"). A different version of the runtime can be used by specifying the runIde.jbrVersion ("jbrVersion" in "Gradle IntelliJ Plugin") attribute, describing a version of the JetBrains Runtime that should be used by the IDE Development Instance. The Gradle plugin will fetch the specified JetBrains Runtime as needed.
A plugin project's plugin.xml file has element values that are "patched" at build time from the attributes of the patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task. As many as possible of the attributes in the Patching DSL will be substituted into the corresponding element values in a plugin project's plugin.xml file:
If a patchPluginXml attribute default value is defined, the attribute value will be patched in plugin.xml regardless of whether the patchPluginXml task appears in the Gradle build script.
For example, the default values for the attributes patchPluginXml.sinceBuild ("sinceBuild" in "Gradle IntelliJ Plugin") and patchPluginXml.untilBuild ("untilBuild" in "Gradle IntelliJ Plugin") are defined based on the declared (or default) value of intellij.version ("version" in "Gradle IntelliJ Plugin"). So by default patchPluginXml.sinceBuild and patchPluginXml.untilBuild are substituted into the <idea-version> ("idea-version" in "Plugin Configuration File") element's since-build and until-build attributes in the plugin.xml file.
If a patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task's attribute value is explicitly defined, the attribute value will be substituted in plugin.xml.
If both patchPluginXml.sinceBuild and patchPluginXml.untilBuild attributes are explicitly set, both are substituted in plugin.xml.
If one attribute is explicitly set (e.g. patchPluginXml.sinceBuild) and one is not (e.g. patchPluginXml.untilBuild has a default value), both attributes are patched at their respective (explicit and default) values.
For no substitution of the <idea-version> element's since-build and until-build attributes, set intellij.updateSinceUntilBuild ("updateSinceUntilBuild" in "Gradle IntelliJ Plugin") to false, and do not provide patchPluginXml.sinceBuild and patchPluginXml.untilBuild values.
The best practice to avoid confusion is to replace the elements in plugin.xml that will be patched by the Gradle plugin with a comment. That way, the values for these parameters do not appear in two places in the source code. The Gradle plugin will add the necessary elements as part of the patching process. For those patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") attributes that contain descriptions such as patchPluginXml.changeNotes ("changeNotes" in "Gradle IntelliJ Plugin") and patchPluginXml.pluginDescription ("pluginDescription" in "Gradle IntelliJ Plugin"), a CDATA block is not necessary when using HTML elements.
To maintain and generate an up-to-date changelog, try using Gradle Changelog Plugin (https://github.com/JetBrains/gradle-changelog-plugin).
As discussed in "Components of a Wizard-Generated Gradle IntelliJ Platform Plugin" in "Creating a Plugin Gradle Project", the Gradle properties project.version, project.group, and rootProject.name are all generated based on the input to the Wizard. However, the Gradle IntelliJ Plugin does not combine and substitute those Gradle properties for the default <id> ("id" in "Plugin Configuration File") and <name> ("name" in "Plugin Configuration File") elements in the plugin.xml file.
The best practice is to keep project.version current. By default, if you modify project.version in Gradle build script, the Gradle plugin will automatically update the <version> ("version" in "Plugin Configuration File") value in the plugin.xml file. This practice keeps all version declarations synchronized.
The Gradle plugin provides tasks that allow for running integrity and compatibility tests:
verifyPluginConfiguration ("verifyPluginConfiguration" in "Gradle IntelliJ Plugin") - validates the versions of SDK, target platform, APIs, etc., configured in a plugin project,
verifyPlugin ("verifyPlugin" in "Gradle IntelliJ Plugin") - validates completeness and contents of plugin.xml descriptors as well as plugin's archive structure,
runPluginVerifier ("runPluginVerifier" in "Gradle IntelliJ Plugin") - runs the IntelliJ Plugin Verifier (https://github.com/JetBrains/intellij-plugin-verifier) tool to check the binary compatibility with specified IntelliJ IDE builds.
Plugin Verifier integration task allows for configuring the exact IDE versions that your plugin will be checked against. See "Plugin Verifier" in "Verifying Plugin Compatibility" for more information.
Please review the Publishing a Plugin page before using the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") task. That documentation explains different ways to use Gradle for plugin uploads without exposing account credentials.
Homepage: Kotlin (https://kotlinlang.org)
Project Template: IntelliJ Platform Plugin Template
This page describes developing plugins using the Kotlin (https://kotlinlang.org) programming language.
Operating on Kotlin codeTo implement a plugin operating on Kotlin code in the IDE, configure Kotlin plugin dependency (Plugin Dependencies) (org.jetbrains.kotlin). See also UAST (UAST - Unified Abstract Syntax Tree) page for information about how to support multiple JVM languages, including Kotlin.
Using Kotlin to write plugins for the IntelliJ Platform is very similar to writing plugins in Java. Existing Java classes can be converted to their Kotlin equivalents by using the J2K converter (https://kotlinlang.org/docs/mixing-java-kotlin-intellij.html#converting-an-existing-java-file-to-kotlin-with-j2k) (part of Kotlin plugin).
In addition to null safety (https://kotlinlang.org/docs/null-safety.html), type-safe builders (https://kotlinlang.org/docs/type-safe-builders.html), and Kotlin Coroutines, the Kotlin language offers many convenient features for plugin development, which make plugins easier to read and simpler to maintain. Much like Kotlin for Android (https://kotlinlang.org/docs/android-overview.html), the IntelliJ Platform makes extensive use of callbacks, which are straightforward to express as lambdas (https://kotlinlang.org/docs/lambdas.html) in Kotlin.
Kotlin classes can be mixed in a project with existing Java code. This might come handy when certain APIs require the use of mentioned Kotlin Coroutines.
Likewise, it is possible to customize the behavior of internal classes in the IntelliJ Platform using extensions (https://kotlinlang.org/docs/extensions.html). For example, it is common practice to guard logging statements (https://www.slf4j.org/faq.html#logging_performance) to avoid the cost of parameter construction, leading to the following ceremony when using the log:
if (logger.isDebugEnabled()) {
logger.debug("..." + expensiveComputation());
}We can achieve the same result more succinctly in Kotlin, by declaring the following extension method:
inline fun Logger.debug(lazyMessage: () -> String) {
if (isDebugEnabled) {
debug(lazyMessage())
}
}Now we can directly write:
logger.debug { "..." + expensiveComputation() }to receive all the benefits of lightweight logging while reducing the code verbosity.
With practice, you will be able to recognize many idioms in the IntelliJ Platform that can be simplified with Kotlin.
The IntelliJ Platform provides a type safe DSL (Kotlin UI DSL Version 2) to build UI forms in a declarative way.
Using UI Designer plugin with Kotlin is not supported (https://youtrack.jetbrains.com/issue/KTIJ-791).
Kotlin Coroutines are a lightweight and easy to implement alternative to threads with many advantages ("Coroutines Advantages" in "Kotlin Coroutines").
The IntelliJ Platform Plugin Template provides a preconfigured project using Kotlin.
IntelliJ IDEA bundles the necessary Kotlin plugin, requiring no further configuration. For detailed instructions, please refer to the Kotlin documentation (https://kotlinlang.org/docs/getting-started.html).
Adding Kotlin source files compilation support to the Gradle-based project requires adding and configuring the Kotlin JVM Gradle plugin (https://kotlinlang.org/docs/gradle.html#targeting-the-jvm).
See the build.gradle.kts from kotlin_demo (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/kotlin_demo) sample plugin:
plugins {
id("java")
id("org.jetbrains.intellij") version "1.17.3"
id("org.jetbrains.kotlin.jvm") version "1.9.23"
}
group = "org.intellij.sdk"
version = "2.0.0"
repositories {
mavenCentral()
}
java {
sourceCompatibility = JavaVersion.VERSION_17
}
// See https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html
intellij {
version.set("2023.2.6")
}
tasks {
buildSearchableOptions {
enabled = false
}
patchPluginXml {
version.set("${project.version}")
sinceBuild.set("232")
untilBuild.set("241.*")
}
compileKotlin {
kotlinOptions.jvmTarget = "17"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "17"
}
}Since Kotlin 1.4, a dependency on the standard library stdlib is added automatically (API Docs (https://kotlinlang.org/api/latest/jvm/stdlib/)). In nearly all cases, it is not necessary to include it in the plugin distribution as the platform already bundles it.
To opt out, add this line in gradle.properties:
kotlin.stdlib.default.dependency = falseThe presence of this Gradle property is checked by the Gradle IntelliJ Plugin with the "verifyPluginConfiguration" in "Gradle IntelliJ Plugin" task. If the property is not present, a warning will be reported during the plugin configuration verification, as it is a common problem when Kotlin stdlib gets bundled within the plugin archive. To bundle stdlib in the plugin distribution, specify explicitly kotlin.stdlib.default.dependency = true.
If a plugin supports multiple platform versions (Build Number Ranges), it must either target the lowest bundled stdlib version or the specific version must be provided in plugin distribution ("Plugin With Dependencies" in "Plugin Content").
IntelliJ Platform version | Bundled stdlib version |
|---|---|
2024.1 | 1.9.22 |
2023.3 | 1.9.10 |
2023.2 | 1.8.20 |
2023.1 | 1.8.0 |
2022.3 | 1.7.0 |
2022.2 | 1.6.21 |
2022.1 | 1.6.20 |
IntelliJ Platform version | Bundled stdlib version |
|---|---|
2021.3 | 1.5.10 |
2021.2 | 1.5.10 |
2021.1 | 1.4.32 |
2020.3 | 1.4.0 |
2020.2 | 1.3.70 |
2020.1 | 1.3.70 |
2019.3 | 1.3.31 |
2019.2 | 1.3.3 |
2019.1 | 1.3.11 |
See Dependency on the standard library (https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library) for more details.
Adding stdlib in testsIf you need to add the Kotlin Standard Library to your test project dependencies, see the "How to test a JVM language?" in "Testing FAQ" section.
Plugins must always use the bundled library from the target IDE and not bundle their own version. Please make sure it is not added via transitive dependencies either (see View and Debug Dependencies (https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html) in Gradle user guide).
See Kotlin Coroutines on how to use them in plugins.
IntelliJ Platform version | Bundled kotlinx-coroutines version |
|---|---|
2024.1 | 1.7.3 |
In general, it is strongly advised to always use the bundled library version.
Please see Third-Party Software and Licenses (https://www.jetbrains.com/legal/third-party-software/) for an overview of all bundled libraries.
The Kotlin Gradle plugin supports incremental compilation (https://kotlinlang.org/docs/gradle-compilation-and-caches.html#incremental-compilation), which allows tracking changes in the source files so the compiler handles only updated code.
No action required.
Remove additional kotlin.incremental.useClasspathSnapshot=false property in gradle.properties if present.
Please consider using Kotlin 1.9.0 or later where this issue has been resolved.
Kotlin 1.8.20 has a new incremental compilation approach (https://kotlinlang.org/docs/gradle-compilation-and-caches.html#a-new-approach-to-incremental-compilation) which is enabled by default. Unfortunately, it is not compatible with the IntelliJ Platform — when reading large JAR files (like app.jar or 3rd-party-rt.jar), leading to an Out of Memory exception:
Execution failed for task ':compileKotlin'.
> Failed to transform app.jar to match attributes {artifactType=classpath-entry-snapshot, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}.
> Execution failed for ClasspathEntrySnapshotTransform: .../lib/app.jar.
> Java heap spaceTo avoid this exception, add the following line to gradle.properties:
kotlin.incremental.useClasspathSnapshot=falsePlugins may use Kotlin classes (https://kotlinlang.org/docs/classes.html) (class keyword) to implement declarations in the plugin configuration file (Plugin Configuration File). When registering an extension, the platform uses a dependency injection framework to instantiate these classes at runtime. For this reason, plugins must not use Kotlin objects (https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview) (object keyword) to implement any plugin.xml (Plugin Configuration File) declarations. Managing the lifecycle of extensions is the platform's responsibility and instantiating these classes as Kotlin singletons may cause issues.
A notable exception is com.intellij.openapi.fileTypes.FileType (com.intellij.fileType extension point), see also the inspection descriptions below.
Problems are highlighted via these inspections (2023.2):
Plugin DevKit | Code | Kotlin object registered as extension for Kotlin code
Plugin DevKit | Plugin descriptor | Extension class is a Kotlin object for plugin.xml
Kotlin companion object is always created once you try to load its containing class, and extension point implementations (Extensions) are supposed to be cheap to create. To avoid unnecessary classloading (and thus slowdown in IDE startup), companion object in extensions must only contain simple constants or logger ("Logging" in "IDE Infrastructure"). Anything else must be a top-level declaration or stored in an object.
Use inspection Plugin DevKit | Code | Companion object in extensions to highlight such problems (2023.3).
How to shorten references (https://intellij-support.jetbrains.com/hc/en-us/community/posts/360010025120-Add-new-parameter-into-kotlin-data-class-from-IDEA-plugin?page=1#community_comment_360002950760)
There are many open-source Kotlin plugins (https://jb.gg/ipe?language=kotlin) built on the IntelliJ Platform. For a readily available source of up-to-date examples of plugins implemented in Kotlin, developers may look to these projects for inspiration:
A JetBrains feature for developing plugins is running or debugging a plugin project from within IntelliJ IDEA. Selecting the runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task for a Gradle-based project (or Run (Running and Debugging a Theme) menu for a DevKit-based project) will launch a Development Instance of the IDE with the plugin enabled. This page describes how to control some settings for the Development Instance.
See also runIde task ("runIde" in "Gradle IntelliJ Plugin") properties and Advanced Configuration (https://www.jetbrains.com/help/idea/tuning-the-ide.html) for general VM options and properties.
An everyday use case is to develop (build) a plugin project against a JDK, e.g., Java 17, and then run or debug the plugin in a Development Instance of the IDE. In such a situation, Development Instance must use a JetBrains Runtime (JBR) (https://www.jetbrains.com/jetbrains-runtime) rather than the JDK used to build the plugin project.
The JetBrains Runtime is an environment for running IntelliJ Platform-based IDEs on Windows, macOS, and Linux. It has some modifications by JetBrains, such as fixes for native crashes not present in official JDK builds. A version of the JetBrains Runtime is bundled with all IntelliJ Platform-based IDEs. To produce accurate results while running or debugging a plugin project in a Development Instance, follow the procedures below to ensure the Development Instance uses a JetBrains Runtime.
By default, the Gradle plugin will fetch and use the version of the JetBrains Runtime for the Development Instance corresponding to the version of the IntelliJ Platform used for building the plugin project. If required, an alternative version can be specified using runIde.jbrVersion ("jbrVersion" in "Gradle IntelliJ Plugin") task property.
The Run Configuration (https://www.jetbrains.com/help/idea/run-debug-configuration.html) for a DevKit-based plugin project controls the JDK used to run and debug a plugin project in a Development Instance. The default Run Configuration uses the same JDK for building the plugin project and running the plugin in a Development Instance.
To change the runtime for the Development Instance, set the JRE field in the Run Configuration edit dialog to download a JetBrains Runtime.
The JetBrains Runtime is determined by the JDK version used to build the plugin project, regardless of whether it is built on macOS, Windows, or Linux.
If a plugin is developed against the Java 8 SE Development Kit 8 for macOS (jdk-8u212-macosx-x64.dmg) to acquire the compatible JetBrains Runtime:
Go to the GitHub JetBrains Runtime Releases (https://github.com/JetBrains/JetBrainsRuntime) for general information and the latest build.
Open the Releases (https://github.com/JetBrains/JetBrainsRuntime/releases) page to access all releases.
Select the package name corresponding to the platform and SDK version. In this case, the package name is jbrsdk8-osx-x64 for JetBrains Runtime SDK version 8, macOS x64 hardware.
In the list of files, find the name that satisfies:
The version and build number match the JDK used to build the plugin project. For example, jbrx-8u252-osx-x64 matches the Java 8 JDK, build 252: jdk-8u252-macosx-x64.
Pick the highest JetBrains Runtime build number available. For example, the file is jbrx-8u252-osx-x64-b1649.2.tar.gz, meaning build 1649.2 for this JetBrains Runtime matching Java 8 JDK build 252.
The JetBrains Runtime is delivered in various variants used for different purposes, like debugging, running for development purposes, or bundling with the IDE.
Available JBR variants are:
jcef - the release bundles with the JCEF (Embedded Browser (JCEF)) browser engine
sdk - JBR SDK bundle used for development purposes
fd - the fastdebug bundle which also includes the jcef module
dcevm - bundles DCEVM (Dynamic Code Evolution Virtual Machine)
nomod – the release bundled without any additional modules
For JBR 17, dcevm is bundled by default. As a consequence, separated dcevm and nomod variants are no longer available.
Starting in 2020.1, this is available for compatible dynamic plugins (Dynamic Plugins). This allows a much faster development cycle by avoiding a full restart of the development instance after detecting code changes (when JARs are modified).
Please note that any unloading problems in a production environment will ask the user to restart the IDE.
Auto-Reload does not work when the sandbox IDE instance is running under a debugger.
Enabled by default for target platform 2020.2 or later.
Set property runIde.autoReloadPlugins ("autoReloadPlugins" in "Gradle IntelliJ Plugin") to true for enabling it in earlier platform versions or false to disable it explicitly, see "How to disable automatic reload of dynamic plugins?" in "Gradle IntelliJ Plugin – FAQ".
After starting the sandbox IDE instance, run buildPlugin ("buildPlugin" in "Gradle IntelliJ Plugin") task after modifications in the plugin project and switch focus back to sandbox instance to trigger reload.
buildSearchableOptions ("buildSearchableOptions" in "Gradle IntelliJ Plugin") task must currently be disabled explicitly ("How to disable building searchable options?" in "Gradle IntelliJ Plugin – FAQ") to workaround Only one instance of IDEA can be run at a time problem.
Add system property idea.auto.reload.plugins in the Plugin DevKit run configuration (Running and Debugging a Theme).
To disable auto-reload, set idea.auto.reload.plugins to false explicitly (2020.1.2+).
The Sandbox Home directory contains the settings, caches, logs, and plugins for a Development Instance of the IDE. This information is stored in a different location than for the installed IDE itself (https://intellij-support.jetbrains.com/hc/en-us/articles/206544519-Directories-used-by-the-IDE-to-store-settings-caches-plugins-and-logs).
The default Sandbox Home location in a plugin Gradle project is:
Windows: $PROJECT_DIRECTORY$\build\idea-sandbox
Linux/macOS: $PROJECT_DIRECTORY$/build/idea-sandbox
The Sandbox Home location can be configured with the intellij.sandboxDir ("sandboxDir" in "Gradle IntelliJ Plugin") property.
For DevKit-based plugins, the default Sandbox Home location is defined in the IntelliJ Platform Plugin SDK. See the Setting Up a Theme Development Environment ("Add IntelliJ Platform Plugin SDK" in "Setting Up a Development Environment") for information about how to set up Sandbox Home in IntelliJ Platform SDK.
The default Sandbox Home directory location is:
Windows: $USER_HOME$\.$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$\system\plugins-sandbox\
Linux: ~/.$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$/system/plugins-sandbox/
macOS: ~/Library/Caches/$PRODUCT_SYSTEM_NAME$$PRODUCT_VERSION$/plugins-sandbox/
Within the Sandbox Home directory are subdirectories of the Development Instance:
config contains settings for the IDE instance.
plugins contains folders for each plugin being run in the IDE instance.
system/caches or system\caches holds the IDE instance data.
system/log or system\log contains the idea.log file for the IDE instance.
Each of these Sandbox Home subdirectories can be manually cleared to reset the IDE Development Instance. At the next launch of a Development Instance, the subdirectories will be repopulated with the appropriate information.
Plugin Signing is a mechanism introduced in the 2021.2 release cycle to increase security in JetBrains Marketplace (https://plugins.jetbrains.com) and all of our IntelliJ-based IDEs.
The Marketplace signing is designed to ensure that plugins are not modified over the course of the publishing and delivery pipeline. If the author does not sign the plugin or has a revoked certificate, a warning dialog will appear in the IDE during installation.
On our side, we will check if the public part of a key is present, and we will verify the signature. This is similar to the Google upload key (https://developer.android.com/studio/publish/app-signing#generate-key) mechanism.
To be sure a file has not been modified, the file will be signed twice – first by the plugin author, then by JetBrains Marketplace.
The plugin author's sign-verify process is as follows:
A plugin author generates a key pair and uploads the public part to JetBrains Marketplace (this feature is not yet available).
A build tool signs a plugin file during the assembly process.
The user uploads the plugin file to JetBrains Marketplace.
JetBrains Marketplace checks if the public key is present in the user profile.
JetBrains Marketplace verifies the signature.
The JetBrains sign-verify process is as follows:
JetBrains CA is used as the source of truth here.
Its public part will be added to the IDE Java TrustStore, while the private part will be used only once to generate an intermediate certificate.
The private key of JetBrains CA is super-secret; in fact, we've already said too much.
The intermediate certificate issues a certificate that will be used to sign plugins. This way, it will be possible to re-generate this certificate without access to JetBrains CA's super-secret private key. The private key of the intermediate certificate is issued and kept in the AWS Certificate Manager, and no application has access to it; people's access is also limited. So now we have an AWS-based Intermediate CA. The public part of the intermediate certificate will be added to the plugin file together with the signing certificate.
The certificate used to sign plugins is stored securely, too. JetBrains Marketplace uses AWS KMS as a signature provider to sign plugin files.
To provide a suitable method for plugin signing, we have introduced the Marketplace ZIP Signer (https://github.com/JetBrains/marketplace-zip-signer) library. It can be executed using the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task provided by the Gradle IntelliJ Plugin if your project is Gradle-based. Alternatively, a standalone CLI Tool can be used.
Both methods require a private certificate key to be already present.
To generate an RSA private.pem private key, run the openssl genpkey command in the terminal, as below:
openssl genpkey\
-aes-256-cbc\
-algorithm RSA\
-out private_encrypted.pem\
-pkeyopt rsa_keygen_bits:4096After that, it's required to convert it into the RSA form with:
openssl rsa\
-in private_encrypted.pem\
-out private.pemAt this point, the generated private.pem content should be provided to the signPlugin.privateKey ("privateKey" in "Gradle IntelliJ Plugin") property. Provided password should be specified as the signPlugin.password ("password" in "Gradle IntelliJ Plugin") property in the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") configuration.
As a next step, we will generate a chain.crt certificate chain with:
openssl req\
-key private.pem\
-new\
-x509\
-days 365\
-out chain.crtThe content of the chain.crt file will be used for the signPlugin.certificateChain ("certificateChain" in "Gradle IntelliJ Plugin") property.
Information about generating a public key based on the private key will be added later, when uploading public keys to JetBrains Marketplace is available.
In version 1.x, the Gradle IntelliJ Plugin provides the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task, which will be executed automatically right before the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") task when signPlugin.certificateChain ("certificateChain" in "Gradle IntelliJ Plugin") and signPlugin.privateKey ("privateKey" in "Gradle IntelliJ Plugin") signing properties are specified. Otherwise, it'll be skipped.
An example signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task configuration may look like:
signPlugin {
certificateChain.set("""
-----BEGIN CERTIFICATE-----
MIIElgCCAn4CCQDo83LWYj2QSTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
...
gdZzxCN8t1EmH8kD2Yve6YKGFCRAIIzveEg=
-----END CERTIFICATE-----
""".trimIndent())
privateKey.set("""
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAwU8awS22Rw902BmwVDDBMlTREX440BAAVM40NW3E0lJ7YTJG
...
EnNBfIVFhh6khisKqTBWSEo5iS2RYJcuZs961riCn1LARztiaXL4l17oW8t+Qw==
-----END RSA PRIVATE KEY-----
""".trimIndent())
password.set("8awS22%#3(4wVDDBMlTREX")
}
publishPlugin {
token.set("perm:a961riC....l17oW8t+Qw==")
}signPlugin {
certificateChain = """
-----BEGIN CERTIFICATE-----
MIIElgCCAn4CCQDo83LWYj2QSTANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJQ
...
gdZzxCN8t1EmH8kD2Yve6YKGFCRAIIzveEg=
-----END CERTIFICATE-----
""".stripIndent()
privateKey = """
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAwU8awS22Rw902BmwVDDBMlTREX440BAAVM40NW3E0lJ7YTJG
...
EnNBfIVFhh6khisKqTBWSEo5iS2RYJcuZs961riCn1LARztiaXL4l17oW8t+Qw==
-----END RSA PRIVATE KEY-----
""".stripIndent()
password = "8awS22%#3(4wVDDBMlTREX"
}
publishPlugin {
token = "perm:a961riC....l17oW8t+Qw=="
}Do not commit your credentials to the Version Control System! To avoid that, you may use environment variables, like:
token.set(providers.environmentVariable("PUBLISH_TOKEN")) password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
Instead of using the signPlugin.privateKey ("privateKey" in "Gradle IntelliJ Plugin") and signPlugin.certificateChain ("certificateChain" in "Gradle IntelliJ Plugin") properties which expect the key and certificate chain content to be provided directly, it's also possible to specify the paths to the files containing the key and certificate chain content. To do that, use the signPlugin.privateKeyFile ("privateKeyFile" in "Gradle IntelliJ Plugin") and signPlugin.certificateChainFile ("certificateChainFile" in "Gradle IntelliJ Plugin") properties instead.
signPlugin {
certificateChainFile.set(file("certificate/chain.crt"))
privateKeyFile.set(file("certificate/private.pem"))
password.set("8awS22%#3(4wVDDBMlTREX")
}
publishPlugin {
token.set("perm:a961riC....l17oW8t+Qw==")
}signPlugin {
certificateChainFile = file("certificate/chain.crt")
privateKeyFile = file("certificate/private.pem")
password = "8awS22%#3(4wVDDBMlTREX"
}
publishPlugin {
token = "perm:a961riC....l17oW8t+Qw=="
}To avoid storing hard-coded values in the project configuration, the most suitable method for local development would be using environment variables provided within the Run/Debug Configuration.
To specify secrets like PUBLISH_TOKEN and values required for the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task, modify your Gradle configuration as follows:
signPlugin {
certificateChain.set(providers.environmentVariable("CERTIFICATE_CHAIN"))
privateKey.set(providers.environmentVariable("PRIVATE_KEY"))
password.set(providers.environmentVariable("PRIVATE_KEY_PASSWORD"))
}
publishPlugin {
token.set(providers.environmentVariable("PUBLISH_TOKEN"))
}signPlugin {
certificateChain = providers.environmentVariable("CERTIFICATE_CHAIN")
privateKey = providers.environmentVariable("PRIVATE_KEY")
password = providers.environmentVariable("PRIVATE_KEY_PASSWORD")
}
publishPlugin {
token = providers.environmentVariable("PUBLISH_TOKEN")
}In the Run/Debug Configuration for publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") Gradle task, provide Environment Variables using relevant environment variable names:

Note that both the private key and certificate chain are multi-line values. It is necessary to transform them first using Base64 encoding before providing the single-line field in the Environment Variables panel.
signPlugin.privateKey ("privateKey" in "Gradle IntelliJ Plugin") and signPlugin.certificateChain ("certificateChain" in "Gradle IntelliJ Plugin") properties will automatically detect and decode the Base64-encoded values.
CLI tool is required if you don't rely on the Gradle IntelliJ Plugin – i.e., when working with Themes (Developing a Theme).
To get the latest Marketplace ZIP Signer CLI Tool, visit the JetBrains/marketplace-zip-signer (https://github.com/JetBrains/marketplace-zip-signer/releases) GitHub Releases page. After downloading the marketplace-zip-signer-cli.jar, execute it as below:
java -jar marketplace-zip-signer-cli.jar sign\
-in "unsigned.zip"\
-out "signed.zip"\
-cert-file "/path/to/chain.crt"\
-key-file "/path/to/private.pem"\
-key-pass "PRIVATE_KEY_PASSWORD"Signing plugins hosted on a custom repository can be accomplished for added trust between the repository and installation. However, unlike Marketplace, the custom repository will not re-sign the plugin with the JetBrains key. Instead, a trusted private CA or self-signed certificate can be used to sign and validate plugins.
Before looking at how we can sign a plugin, let's first review how verification works when a non-JetBrains certificate is used. As of 2021.2, during verification, IntelliJ-based IDEs check if the plugin was signed by the JetBrains CA certificate or any public key provided by the user via Settings | Plugins | Manage Plugin Certificates. In 2021.2.1, a system property has been added: intellij.plugins.truststore, pointing to a trusted JKS TrustStore. During verification, the plugin's public key is extracted from the signature. The last certificate entry in the chain matched against the certificates stored in one of the storages from above.
If an internal CA is available, you can use this to generate certificates for signing. When choosing this route, the certificate chain includes the root CA public key at the end of the chain.
With this approach, existing internal TrustStores may exist and could be used. Be sure when choosing a TrustStore that the CAs are limited to the internal CAs you trust. Using a TrustStore with public CAs can expose the users to an attack vector.
If adding a TrustStore to a user's environment is not possible, the user may also add the root CAs public key to Settings | Plugins | Manage Plugin Certificates.
Using a self-signed certificate is an option if no internal CAs exist. To generate the key and public key, see: Generate Private Key
If providing users with a TrustStore, you can generate one with the public key using keytool:
keytool -import -alias IdeaPlugin -file chain.crt -keystore pluginKeystore.jks -storepass changeit(note: the TrustStore password must remain changeit)
Otherwise, users may add the public key manually to Settings | Plugins | Manage Plugin Certificates.
To verify the signature of a plugin, you can use the verifyPluginSignature ("verifyPluginSignature" in "Gradle IntelliJ Plugin") task.
By default, this task will use the same certificate chain as provided to the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task in the previous section.
To verify the signature using CLI tool, execute the verify command as below:
java -jar marketplace-zip-signer-cli.jar verify\
-in "signed.zip"\
-cert "/path/to/chain.crt"When your plugin is ready, you can publish it to a JetBrains Marketplace (https://plugins.jetbrains.com) plugin repository so that other users can install it in IDE. The first plugin publication, even when a project uses the Gradle setup, must be uploaded manually.
Before publishing a plugin, ensure it follows all recommendations from Plugin User Experience (UX). For an optimal presentation, see the guidelines from Plugin Overview page (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html). The Busy Plugin Developers. Episode 2 discusses 5 tips for optimizing JetBrains Marketplace plugin page (https://youtu.be/oB1GA9JeeiY?t=52) in more detail. See also Marketing about widgets and badges.
If you plan to publish your plugin to a repository other than the JetBrains Marketplace (https://plugins.jetbrains.com), please refer to the Custom Plugin Repository documentation.
If your plugin ships with additional libraries ("Plugin With Dependencies" in "Plugin Content"), do not repackage them into the main plugin archive. Otherwise, Plugin Verifier (Verifying Plugin Compatibility) will yield false positives for unresolved classes and methods.
Before publishing your plugin, make sure it is signed. For more details on generating a proper certificate and configuring the signing process, check the Plugin Signing (Plugin Signing) article.
To upload your plugin to the JetBrains Marketplace (https://plugins.jetbrains.com), you must log in with your personal JetBrains Account.
Open the JetBrains Account Center (https://account.jetbrains.com) and click Create Account.
Fill in all fields in the Create JetBrains Account form that opens and click Register.
To upload your plugin to JetBrains Marketplace:
Log in to JetBrains Marketplace (https://plugins.jetbrains.com/author/me) with your personal JetBrains account.
On your Profile page that opens, click Add new plugin.
Fill in the Add new plugin form that opens and click the Add the plugin button to upload your plugin.
See also Marketplace Docs (https://plugins.jetbrains.com/docs/marketplace/uploading-a-new-plugin.html).
New versions can be uploaded manually on the plugin's detail page, see Marketplace Docs (https://plugins.jetbrains.com/docs/marketplace/plugin-updates.html) for details.
Once you have configured Gradle support (Configuring Gradle IntelliJ Plugin), and uploaded the plugin to the plugin repository at least once, you can automatically build and deploy your plugin to the JetBrains Marketplace (https://plugins.jetbrains.com).
For initial upload, manual distribution or local installation, invoke the buildPlugin ("buildPlugin" in "Gradle IntelliJ Plugin") Gradle task to create the plugin distribution. If you configured you project to rely on Plugin Signing, use the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task instead.
The resulting ZIP file is located in build/distributions and can then be installed via Install Plugin from Disk... (https://www.jetbrains.com/help/idea/managing-plugins.html#install_plugin_from_disk) action or uploaded to a Custom Plugin Repository.
To deploy a plugin to the JetBrains Marketplace, you need to supply your Personal Access Token, which you can find on your profile page, in My Tokens (https://plugins.jetbrains.com/author/me/tokens) section.
To create a new token, provide its name and click the Generate Token button. A new token will be created and displayed right below.
Copy it before you close this page and keep it in a secure location. This is the only time the token is visible.
This section describes two options to supply your Personal Access Token via Gradle using:
Environment variables,
Parameters to the Gradle task.
Start by defining an environment variable such as:
export ORG_GRADLE_PROJECT_intellijPublishToken='YOUR_TOKEN'On macOS systems, environment variables set in .bash_profile are only visible to processes you run from bash. Environment variables visible to all processes need to be defined in Environment.plist (https://developer.apple.com/library/archive/qa/qa1067/_index.html).
Now provide the environment variable in the run configuration with which you run the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") task locally. To do so, create a Gradle run configuration (if not already done), choose your Gradle project, specify the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") task, and then add the environment variable.
publishPlugin {
token.set(System.getenv("ORG_GRADLE_PROJECT_intellijPublishToken"))
}publishPlugin {
token = System.getenv("ORG_GRADLE_PROJECT_intellijPublishToken")
}Note that you still need to put some default values (can be empty) in the Gradle properties because otherwise, you will get a compilation error.
Like using environment variables, you can also pass your token as a parameter to the Gradle task. For example, you can provide the parameter
-Dorg.gradle.project.intellijPublishToken=YOUR_TOKENon the command line or by putting it in the arguments of your Gradle run configuration.
Note that also, in this case, you still need to put some default values in your Gradle properties.
The first step when deploying a plugin is to confirm that it works correctly. You may wish to verify this by installing your plugin from disk (https://www.jetbrains.com/help/idea/managing-plugins.html) on a fresh instance of your target IDE(s).
The Marketplace signing is designed to ensure that plugins are not modified over the course of the publishing and delivery pipeline. In version 1.x, the Gradle IntelliJ Plugin provides the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task, which will be executed automatically right before the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin").
For more details on generating a proper certificate and configuring the signPlugin ("signPlugin" in "Gradle IntelliJ Plugin") task, check the Plugin Signing (Plugin Signing) article.
Once you are confident the plugin works as intended, make sure the plugin version is updated, as the JetBrains Marketplace won't accept multiple artifacts with the same version.
To deploy a new version of your plugin to the JetBrains Marketplace, invoke the publishPlugin ("publishPlugin" in "Gradle IntelliJ Plugin") Gradle task.
Now check the most recent version of your plugin on the JetBrains Marketplace (https://plugins.jetbrains.com/). If successfully deployed, any users who currently have your plugin installed on an available version of the IntelliJ Platform are notified of a new update available as soon as the update has been verified.
You may also deploy plugins to a release channel of your choosing, by configuring the publishPlugin.channels ("channels" in "Gradle IntelliJ Plugin") property. For example:
publishPlugin {
channels.set(listOf("beta"))
}publishPlugin {
channels = ['beta']
}When empty, this uses the default plugin repository, available to all JetBrains Marketplace (https://plugins.jetbrains.com/) users. However, you can publish it to an arbitrarily-named channel. These non-default release channels are treated as separate repositories.
When using a non-default release channel, users need to configure a new custom plugin repository (https://www.jetbrains.com/help/idea/managing-plugins.html#repos) in their IDE to install your plugin. For example, if you specify publishPlugin.channels = ['canary'], then users need to add the https://plugins.jetbrains.com/plugins/canary/list repository to install the plugin and receive updates.
Popular channel names include:
alpha: https://plugins.jetbrains.com/plugins/alpha/list
beta: https://plugins.jetbrains.com/plugins/beta/list
eap: https://plugins.jetbrains.com/plugins/eap/list
More information about the available configuration options is in the documentation of the IntelliJ Gradle Plugin ("publishPlugin" in "Gradle IntelliJ Plugin").
Plugin distribution will be built using Gradle ("buildPlugin" in "Gradle IntelliJ Plugin") or Plugin DevKit (Deploying a Theme).
The plugin distribution .jar file contains:
configuration file (META-INF/plugin.xml) (Plugin Configuration File (Plugin Configuration File))
classes implementing the plugin functionality
recommended: plugin logo file(s) (META-INF/pluginIcon*.svg) (Plugin Logo (Plugin Logo))
See "Distribution Size" in "Plugin User Experience (UX)" for important steps to optimize the plugin distribution file.
Targeting a plugin distribution to a specific OS is not possible (issue (https://youtrack.jetbrains.com/issue/MP-1896)).
A plugin consisting of a single .jar file is placed in the /plugins directory.
The plugin .jar file is placed in the /lib folder under the plugin's "root" folder, together with all required bundled libraries.
All jars from the /lib folder are automatically added to the classpath (see also Plugin Class Loaders (Class Loaders)).
Do Not Repackage LibrariesDo not repackage libraries into the main plugin archive (sample.jar in the sample below). Otherwise, Plugin Verifier (Verifying Plugin Compatibility) will yield false positives for unresolved classes and methods.
If a plugin exposes its own API that is meant to be used by other plugins, it is worth considering bundling the plugin's API sources in the ZIP distribution.
If a third-party plugin uses Gradle IntelliJ Plugin and adds a dependency to the plugin which bundles sources in the ZIP distribution, sources will be automatically attached to the plugin library and visible in IDE when developers navigate to the API classes. Being able to see API sources drastically improves the development experience, and it is highly recommended to bundle them.
Attaching bundled plugin sources in IDE is available starting with Gradle IntelliJ Plugin 1.7.0.
The API source JARs must be located in the example-plugin.zip!/plugin/lib/src directory in the plugin ZIP distribution, e.g.:
The plugin distribution ZIP file can contain multiple source JARs, and there are no strict rules for the source JAR names.
Usually, the following classes are considered as plugin API:
Extension Point (Extension Points) and related classes
Listener (Listeners) and related classes
Services (Services) and utilities that provide access to the plugin data/behavior
Keep in mind that API should be stable and change very rarely as every incompatible change will break the client plugins. It is also recommended to organize the plugin code in multiple modules with clear responsibilities, e.g.:
example-plugin-api - a module containing API
example-plugin-impl - a module containing plugin features code that are not meant to be extended or used by client plugins
General rule to define API is to include classes that are likely to be consumed by the client plugins code.
Of course, more complex plugins may require more fine-grained structure. See Gradle IntelliJ Plugin - Usage Examples (Gradle IntelliJ Plugin – Usage Examples).
In the simplest case, if a project consists of a single module and plugin API is clearly isolated in a package, e.g. com.example.plugin.openapi, including the source JAR can be achieved by adding the following snippet to the tasks section of the Gradle build script:
tasks {
val createOpenApiSourceJar by registering(Jar::class) {
// Java sources
from(sourceSets.main.get().java) {
include("**/com/example/plugin/openapi/**/*.java")
}
// Kotlin sources
from(kotlin.sourceSets.main.get().kotlin) {
include("**/com/example/plugin/openapi/**/*.kt")
}
destinationDirectory.set(layout.buildDirectory.dir("libs"))
archiveClassifier.set("src")
}
buildPlugin {
dependsOn(createOpenApiSourceJar)
from(createOpenApiSourceJar) { into("lib/src") }
}
}task createOpenApiSourceJar(type: Jar) {
// Java sources
from(sourceSets.main.java) {
include '**/com/example/plugin/openapi/**/*.java'
}
// Kotlin sources
from(sourceSets.main.kotlin) {
include '**/com/example/plugin/openapi/**/*.kt'
}
destinationDirectory = layout.buildDirectory.dir('libs')
archiveClassifier = 'src'
}
buildPlugin {
dependsOn(createOpenApiSourceJar)
from(createOpenApiSourceJar) { into 'lib/src' }
}The above configuration will create a source JAR containing Java and Kotlin source files from the com.example.plugin.openapi package and add it to the final plugin ZIP distribution in the required example-plugin.zip!/example-plugin/lib/src directory.
If your plugin is a Gradle project and there is no clear open API package separation, it is recommended to restructure the plugin project to a Gradle multi-project variant and create a dedicated open API subproject that contains all API sources to be included in the final distribution created by the main plugin Gradle project.
A separate class loader is used to load the classes of each plugin. This allows each plugin to use a different library version, even if the same library is used by the IDE itself or by another plugin.
Third-Party Software and Licenses (https://www.jetbrains.com/legal/third-party-software/) lists all bundled libraries and their versions for each product.
Gradle 7 introduced implementation scope, replacing compile scope. For this setup, to use project defined dependency instead of the bundled IDE version, add the following snippet to your Gradle build script:
configurations.all {
resolutionStrategy.sortArtifacts(ResolutionStrategy.SortOrder.DEPENDENCY_FIRST)
}configurations.all {
resolutionStrategy.sortArtifacts(ResolutionStrategy.SortOrder.DEPENDENCY_FIRST)
}By default, the main IDE class loader loads classes that are not found in the plugin class loader. However, in the plugin.xml (Plugin Configuration File) file, you may use the <depends> ("depends" in "Plugin Configuration File") element to specify that a plugin depends (Plugin Dependencies) on one or more other plugins. In this case, the class loaders of those plugins will be used for classes not found in the current plugin. This allows a plugin to reference classes from other plugins.
Some libraries use ServiceLoader (https://docs.oracle.com/javase/8/docs/api/index.html?java/util/ServiceLoader.html) to detect and load implementations. For this to work in a plugin, the context class loader must be set to the plugin's classloader and restored afterwards with the original one around initialization code:
Thread currentThread = Thread.currentThread();
ClassLoader originalClassLoader = currentThread.getContextClassLoader();
ClassLoader pluginClassLoader = this.getClass().getClassLoader();
try {
currentThread.setContextClassLoader(pluginClassLoader);
// code working with ServiceLoader here
} finally {
currentThread.setContextClassLoader(originalClassLoader);
}The IntelliJ Platform provides the concept of actions. An action is a class derived from AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java), whose actionPerformed() method is called when its menu item or toolbar button is selected.
Actions are the most common way for a user to invoke the functionality of your plugin. An action can be invoked from a menu or a toolbar, using a keyboard shortcut or the Help | Find Action... lookup.
Actions are organized into groups, which, in turn, can contain other groups. A group of actions can form a toolbar or a menu. Subgroups of the group can form submenus of a menu.
The user can customize all registered actions via Menus and Toolbars (https://www.jetbrains.com/help/idea/customize-actions-menus-and-toolbars.html) settings.
Please see Action System (Actions) on how to create and register actions in the IDE.
Extensions are the most common way for a plugin to extend the IntelliJ Platform's functionality in a way that is not as straightforward as adding an action to a menu or toolbar.
The following are some of the most common tasks accomplished using extensions:
The com.intellij.toolWindow extension point allows plugins to add tool windows (Tool Windows) (panels displayed at the sides of the IDE user interface);
The com.intellij.applicationConfigurable and com.intellij.projectConfigurable extension points allow plugins to add pages to the Settings dialog (Settings);
Custom language plugins (Custom Language Support) use many extension points to extend various language support features in the IDE.
There are more than 1500 extension points available in the platform and the bundled plugins, allowing customizing different parts of the IDE behavior.
IntelliJ Platform Extension Point and Listener List and IntelliJ Community Plugins Extension Point and Listener List list all available extension points in IntelliJ Platform and from bundled plugins in IntelliJ IDEA. Additionally, dedicated Extension Point and Listener Lists specific to IDEs are available under Product Specific. Browse usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Alternatively (or when using 3rd party extension points), all available extension points for the specified namespace (defaultExtensionNs) can be listed using auto-completion inside the <extensions> ("extensions" in "Plugin Configuration File") block in plugin.xml (Plugin Configuration File). Use View | Quick Documentation in the lookup list to access more information about the extension point and implementation (if applicable). See Explore the IntelliJ Platform API for more information and strategies.
Auto-completion, Quick Documentation, and other code insight features are available on extension point tags and attributes in plugin.xml.
Add an <extensions> ("extensions" in "Plugin Configuration File") element to your plugin.xml if it's not yet present there. Set the defaultExtensionNs attribute to one of the following values:
com.intellij if your plugin extends the IntelliJ Platform core functionality.
{ID of a plugin} if your plugin extends the functionality of another plugin (must configure Plugin Dependencies (Plugin Dependencies)).
Add a new child element to the <extensions> element. The child element's name must match the name of the extension point you want the extension to access.
Depending on the type of the extension point, do one of the following:
If the extension point was declared using the interface attribute, set the implementation attribute to the name of the class that implements the specified interface.
If the extension point was declared using the beanClass attribute, set all properties annotated with the @Attribute (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Attribute.java) and Tag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Tag.java) annotations in the specified bean class.
See the "Declaring Extension Points" in "Extension Points" section for details.
Implement the extension API as required (see ).
To clarify this procedure, consider the following sample section of the plugin.xml file that defines two extensions designed to access the com.intellij.appStarter and com.intellij.projectTemplatesFactory extension points in the IntelliJ Platform, and one extension to access the another.plugin.myExtensionPoint extension point in another plugin another.plugin:
<!--
Declare extensions to access extension points in the IntelliJ Platform.
These extension points have been declared using "interface".
-->
<extensions defaultExtensionNs="com.intellij">
<appStarter
implementation="com.example.MyAppStarter"/>
<projectTemplatesFactory
implementation="com.example.MyProjectTemplatesFactory"/>
</extensions>
<!--
Declare extensions to access extension points in a custom plugin "another.plugin".
The "myExtensionPoint" extension point has been declared using "beanClass"
and exposes custom properties "key" and "implementationClass".
-->
<extensions defaultExtensionNs="another.plugin">
<myExtensionPoint
key="keyValue"
implementationClass="com.example.MyExtensionPointImpl"/>
</extensions>Please note the following important points:
Extension implementation must be stateless. Use explicit Services for managing (runtime) data.
Avoid any initialization in the constructor, see also notes for Services ("Constructor" in "Services").
Do not perform any static initialization. Use inspection Plugin DevKit | Code | Static initialization in extension point implementations (2023.3).
An extension implementation must not be registered as Service (Services) additionally. Use inspection Plugin DevKit | Code | Extension registered as service/component (2023.3).
When using Kotlin (Configuring Kotlin Support):
Do not use object but class for implementation. More details ("Do not use object but class" in "Configuring Kotlin Support")
Do not use companion object to avoid excessive classloading/initialization when the extension class is loaded. Use top-level declarations or objects instead. More details ("Do not use companion object in extensions" in "Configuring Kotlin Support")
Unique ID. Consider prepending ID with the prefix related to the plugin name or ID to not clash with other plugins defining extensions with the same ID, e.g., com.example.myplugin.myExtension.
Allows ordering all defined extensions using first, last or before|after [id] respectively.
Allows restricting an extension to given OS, e.g., os="windows" registers the extension on Windows only
If an extension instance needs to "opt out" in certain scenarios, it can throw ExtensionNotApplicableException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/extensions/ExtensionNotApplicableException.java) in its constructor.
Several tooling features are available to help configure bean class extension points in plugin.xml.
Properties annotated with RequiredElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/extensions/RequiredElement.java) are inserted automatically and validated (2019.3 and later). If the given property is allowed to have an explicit empty value, set allowEmpty to true (2020.3 and later).
Property names matching the following list will resolve to fully qualified class name:
implementation
className
serviceInterface/serviceImplementation
ending with Class (case-sensitive)
A required parent type can be specified in the extension point declaration via nested <with> ("with" in "Plugin Configuration File"):
<extensionPoint name="myExtension" beanClass="MyExtensionBean">
<with
attribute="psiElementClass"
implements="com.intellij.psi.PsiElement"/>
</extensionPoint>Property name language (or ending in *Language, 2020.2+) resolves to all present Language IDs.
Similarly, action resolves to all registered <action> ("action" in "Plugin Configuration File") IDs.
Annotating with @Nls (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/Nls.java) validates a UI String capitalization according to the text property Capitalization enum value (2019.2 and later).
Properties marked as @Deprecated or annotated with any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Internal, @Experimental, @ScheduledForRemoval, or @Obsolete will be highlighted accordingly.
Attributes with Enum type support code insight with lowerCamelCased notation (2020.1 and later). Note: these must not override toString().
A service is a plugin component loaded on demand when your plugin calls the getService() method of corresponding ComponentManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/components/ComponentManager.java) instance (see Types). The IntelliJ Platform ensures that only one instance of a service is loaded even though it is called several times. Services are used to encapsulate logic operating on a set of related classes or to provide some reusable functionality that can be used across the plugin project. Conceptually, they don't differ from the service classes in other languages or frameworks.
A service must have an implementation class used for service instantiation. A service may also have an interface class used to obtain the service instance and provide the service's API.
A service needing a shutdown hook/cleanup routine can implement Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java) and perform necessary work in dispose() (see "Automatically Disposed Objects" in "Disposer and Disposable").
Services as APIIf declared services are intended to be used by other plugins depending on your plugin, consider bundling their sources (Bundling Plugin API Sources) in the plugin distribution.
The IntelliJ Platform offers three types of services: application-level services (global singleton), project-level services, and module-level services. For the latter two, a separate instance of the service is created for each instance of its corresponding scope, see Project Model Introduction (Project Structure).
Avoid using module-level services as it can increase memory usage for projects with many modules.
To improve startup performance, avoid any heavy initializations in the constructor.
Project/Module-level service constructors can have a Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java)/Module (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/module/Module.java) argument.
Do not use Constructor InjectionUsing constructor injection of dependency services is deprecated (and not supported in ) for performance reasons.
Other service dependencies must be acquired only when needed in all corresponding methods, e.g., if you need a service to get some data or execute a task, retrieve the service before calling its methods. Do not retrieve services in constructors to store them in class fields.
Use inspection Plugin DevKit | Code | Non-default constructors for service and extension class to verify code.
When using Kotlin Coroutines, a distinct service scope (Coroutine Scopes) can be injected as parameter.
The Application Service and Project Service scopes are bound to an application and project service ("Types" in "Services") lifetimes accordingly. They are children of the "Intersection Scopes" in "Coroutine Scopes", which means that they are canceled when the application/project is closed or a plugin is unloaded.
The service scope is provided to services via constructor injection. The following constructor signatures are supported:
MyService(CoroutineScope) for application and project services
MyProjectService(Project, CoroutineScope) for project services
Each service instance receives its own scope instance. The injected scopes' contexts contain Dispatchers.Default (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) and CoroutineName(serviceClass) (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/).
See "Launching Coroutine From Service Scope" in "Launching Coroutines" for full samples.
A service not going to be overridden/exposed as API to other plugins does not need to be registered in plugin.xml (Plugin Configuration File) (see ). Instead, annotate service class with @Service (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/components/Service.java) (see ). The service instance will be created in scope according to the caller (see ).
None of these attributes is required: os, client, overrides, id, preload.
Service class must be final.
Constructor injection of dependency services is not supported.
If application-level service is a PersistentStateComponent (Persisting State of Components), roaming must be disabled (roamingType = RoamingType.DISABLED).
Use these inspections to verify these and highlight services that can be converted (2023.3):
Plugin DevKit | Code | Light service must be final
Plugin DevKit | Code | Mismatch between light service level and its constructor
Plugin DevKit | Code | A service can be converted to a light one and corresponding Plugin DevKit | Plugin descriptor | A service can be converted to a light one for plugin.xml
Application-level light service:
@Service
public final class MyAppService {
public void doSomething(String param) {
// ...
}
}Project-level light service example:
@Service(Service.Level.PROJECT)
public final class MyProjectService {
private final Project myProject;
MyProjectService(Project project) {
myProject = project;
}
public void doSomething(String param) {
String projectName = myProject.getName();
// ...
}
}Application-level light service:
@Service
class MyAppService {
fun doSomething(param: String) {
// ...
}
}Project-level light service example:
@Service(Service.Level.PROJECT)
class MyProjectService(private val project: Project) {
fun doSomething(param: String) {
val projectName = project.name
// ...
}
}To register a non-Light Service, distinct extension points are provided for each type:
com.intellij.applicationService - application-level service
com.intellij.projectService - project-level service
com.intellij.moduleService - module-level service (not recommended, see Note)
To expose service API, create a separate class for serviceInterface and extend it in corresponding class registered in serviceImplementation. If serviceInterface isn't specified, it's supposed to have the same value as serviceImplementation. Use inspection Plugin DevKit | Plugin descriptor | Plugin.xml extension registration to highlight redundant serviceInterface declarations.
To provide a custom implementation for test/headless environment, specify testServiceImplementation/headlessImplementation additionally.
Application-level service:
Interface:
public interface MyAppService {
void doSomething(String param);
}Implementation:
final class MyAppServiceImpl implements MyAppService {
@Override
public void doSomething(String param) {
// ...
}
}Project-level service:
Interface:
public interface MyProjectService {
void doSomething(String param);
}Implementation:
final class MyProjectServiceImpl implements MyProjectService {
private final Project myProject;
MyProjectServiceImpl(Project project) {
myProject = project;
}
public void doSomething(String param) {
String projectName = myProject.getName();
// ...
}
}Application-level service:
Interface:
interface MyAppService {
fun doSomething(param: String)
}Implementation:
internal class MyAppServiceImpl : MyAppService {
override fun doSomething(param: String) {
// ...
}
}Project-level service:
Interface:
interface MyProjectService {
fun doSomething(param: String)
}Implementation:
internal class MyProjectServiceImpl(private val project: Project)
: MyProjectService {
fun doSomething(param: String) {
val projectName = project.name
// ...
}
}Registration in plugin.xml:
<extensions defaultExtensionNs="com.intellij">
<!-- Declare the application-level service -->
<applicationService
serviceInterface="com.example.MyAppService"
serviceImplementation="com.example.MyAppServiceImpl"/>
<!-- Declare the project-level service -->
<projectService
serviceInterface="com.example.MyProjectService"
serviceImplementation="com.example.MyProjectServiceImpl"/>
</extensions>Correct Service RetrievalNever acquire service instances prematurely or store them in fields for later use. Instead, always obtain service instances directly and only at the location where they're needed. Failing to do so will lead to unexpected exceptions and severe consequences for the plugin's functionality.
Such problems are highlighted via inspections (2023.3):
Plugin DevKit | Code | Application service assigned to a static final field or immutable property
Plugin DevKit | Code | Incorrect service retrieving
Plugin DevKit | Code | Simplifiable service retrieving
Getting a service doesn't need a read action and can be performed from any thread. If a service is requested from several threads, it will be initialized in the first thread, and other threads will be blocked until it is fully initialized.
MyAppService applicationService =
ApplicationManager.getApplication().getService(MyAppService.class);
MyProjectService projectService =
project.getService(MyProjectService.class);Service implementations can wrap these calls with convenient static getInstance() or getInstance(Project) method:
MyAppService applicationService = MyAppService.getInstance();
MyProjectService projectService = MyProjectService.getInstance(project);val applicationService = service<MyAppService>()
val projectService = project.service<MyProjectService>()To clarify how to use services, consider the maxOpenProjects sample plugin available in the code samples (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/max_opened_projects).
This plugin has an application service counting the number of currently opened projects in the IDE. If this number exceeds the maximum number of simultaneously opened projects allowed by the plugin (3), it displays an information message.
See Code Samples (Code Samples) on how to set up and run the plugin.
Listeners allow plugins to subscribe to events delivered through the message bus (see Messaging infrastructure (Messaging Infrastructure) for details).
Listeners are defined at application (global) or project (Project) level.
All available listeners/topics are listed on IntelliJ Platform Extension Point and Listener List and IntelliJ Platform Extension Point and Listener List under Listeners sections. Browse usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener implementations must be stateless and may not implement life-cycle (e.g., Disposable). Use inspection Plugin DevKit | Code | Listener implementation implements 'Disposable' to verify (2023.3).
Declarative registration of listeners (2019.3 and later) allows achieving better performance than registering listeners from code. The advantage is because listener instances get created lazily — the first time an event is sent to the topic — and not during application startup or project opening.
To define an application-level listener, add the <applicationListeners> ("applicationListeners" in "Plugin Configuration File") section to plugin.xml (Plugin Configuration File):
<idea-plugin>
<applicationListeners>
<listener
class="myPlugin.MyListenerClass"
topic="BaseListenerInterface"/>
</applicationListeners>
</idea-plugin>The topic attribute specifies the listener interface corresponding to the type of events to receive. Usually, this is the interface used as the type parameter of the Topic (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/util/messages/Topic.java) instance for the type of events. The class attribute specifies the class in the plugin that implements the listener interface and receives the events.
As a specific example, to receive events about all Virtual File System (Virtual File System) changes, implement the BulkFileListener interface, corresponding to the topic VirtualFileManager.VFS_CHANGES. To subscribe to this topic from code, use something like the following snippet:
messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES,
new BulkFileListener() {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
// handle the events
}
});To use declarative registration, it's no longer required to reference the Topic instance. Instead, refer directly to the listener interface class:
<applicationListeners>
<listener
class="myPlugin.MyVfsListener"
topic="com.intellij.openapi.vfs.newvfs.BulkFileListener"/>
</applicationListeners>Then provide the listener implementation:
package myPlugin;
final class MyVfsListener implements BulkFileListener {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
// handle the events
}
}Project (Project)-level listeners are registered in the same way, except that the top-level tag is <projectListeners> ("projectListeners" in "Plugin Configuration File"). They can be used to listen to project-level events, for example, tool window (Tool Windows) operations:
<idea-plugin>
<projectListeners>
<listener
class="myPlugin.MyToolWindowListener"
topic="com.intellij.openapi.wm.ex.ToolWindowManagerListener"/>
</projectListeners>
</idea-plugin>The class implementing the listener interface can define a one-argument constructor accepting a Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java), and it will receive the instance of the project for which the listener is created:
package myPlugin;
final class MyToolWindowListener implements ToolWindowManagerListener {
private final Project project;
MyToolWindowListener(Project project) {
this.project = project;
}
@Override
public void stateChanged(@NotNull ToolWindowManager toolWindowManager) {
// handle the state change
}
}Registration of listeners can be restricted using the following attributes.
Allows restricting listener to given OS, e.g., os="windows" for Windows only (2020.1 and later)
Set to false to disable listener if Application.isUnitTestMode() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) returns true
Set to false to disable listener if Application.isHeadlessEnvironment() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) returns true. Also covers activeInTestMode as test mode implies headless mode.
If declared listener topics are intended to be used by other plugins depending on your plugin, consider bundling their sources (Bundling Plugin API Sources) in the plugin distribution.
See Plugin Extensions (Extensions) for using extension points in your plugin.
By defining extension points in your plugin, you can allow other plugins to extend your plugin's functionality. There are two types of extension points:
Interface extension points allow other plugins to extend your plugins with code. When defining an interface extension point, specify an interface, and other plugins will provide classes implementing that interface. The providing plugin can then invoke methods on this interface. In most cases, the interface can be annotated with ApiStatus.@OverrideOnly (see "Override-Only API" in "Verifying Plugin Compatibility").
Bean extension points allow other plugins to extend a plugin with data. Specify the fully qualified name of an extension class, and other plugins will provide data that will be turned into instances of that class.
You can declare extensions and extension points in the plugin configuration file plugin.xml (Plugin Configuration File), within the <extensions> ("extensions" in "Plugin Configuration File") and <extensionPoints> ("extensionPoints" in "Plugin Configuration File") sections.
To declare extension points in your plugin, add an <extensionPoints> section to your plugin.xml. Then insert a child element <extensionPoint> ("extensionPoint" in "Plugin Configuration File") that defines the extension point name and the name of a bean class or an interface that is allowed to extend the plugin functionality in the name, beanClass and interface attributes, respectively.
myPlugin/META-INF/plugin.xml
<idea-plugin>
<id>my.plugin</id>
<extensionPoints>
<extensionPoint
name="myExtensionPoint1"
beanClass="com.example.MyBeanClass"/>
<extensionPoint
name="myExtensionPoint2"
interface="com.example.MyInterface"/>
</extensionPoints>
</idea-plugin>The name attribute assigns a unique name for this extension point. Its fully qualified name required in Using Extension Points is built by prefixing the plugin <id> ("id" in "Plugin Configuration File") as "namespace" followed by . separator: my.plugin.myExtensionPoint1 and my.plugin.myExtensionPoint2.
The beanClass attribute sets a bean class that specifies one or several properties annotated with the @Attribute (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Attribute.java) annotation. Note that bean classes do not follow the JavaBean standard. Implement PluginAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/extensions/PluginAware.java) to obtain information about the plugin providing the actual extension (see ).
Alternatively, the interface attribute sets an interface the plugin that contributes to the extension point must then implement.
The area attribute determines the scope in which the extension will be instantiated. As extensions should be stateless, it is not recommended to use non-default. Must be one of IDEA_APPLICATION for Application (default), IDEA_PROJECT for Project, or IDEA_MODULE for Module scope.
The plugin that contributes to the extension point will read those properties from the plugin.xml file.
If extension implementations are filtered according to dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"), the base class should be marked with PossiblyDumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/PossiblyDumbAware.java) to highlight this.
Base classes for extensions requiring a key:
LanguageExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/LanguageExtension.java)
FileTypeExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeExtension.java)
ClassExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/util/ClassExtension.java)
KeyedExtensionCollector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/util/KeyedExtensionCollector.java)
See Bundling Plugin API Sources section explaining how to expose extension points sources to other plugins.
To clarify this, consider the following sample MyBeanClass bean class used in the above plugin.xml file:
myPlugin/src/com/myplugin/MyBeanClass.java
public final class MyBeanClass extends AbstractExtensionPointBean {
@Attribute("key")
public String key;
@Attribute("implementationClass")
public String implementationClass;
public String getKey() {
return key;
}
public String getClass() {
return implementationClass;
}
}See Extension properties code insight ("Extension Properties Code Insight" in "Extensions") on how to provide smart completion/validation.
For above extension points usage in anotherPlugin would look like this (see also Declaring Extensions ("Declaring Extensions" in "Extensions")):
anotherPlugin/META-INF/plugin.xml
<idea-plugin>
<id>another.plugin</id>
<!-- Declare dependency on plugin defining extension point: -->
<depends>my.plugin</depends>
<!-- Use "my.plugin" namespace: -->
<extensions defaultExtensionNs="my.plugin">
<myExtensionPoint1
key="someKey"
implementationClass="another.some.implementation.class"/>
<myExtensionPoint2
implementation="another.MyInterfaceImpl"/>
</extension>
</idea-plugin>To refer to all registered extension instances at runtime, declare an ExtensionPointName (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPointName.kt) with private visibility passing in the fully qualified name matching its declaration in plugin.xml. If needed, provide a public method to query registered extensions (Sample: TestSourcesFilter.isTestSources() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java)).
myPlugin/src/com/myplugin/MyExtensionUsingService.java
@Service
public final class MyExtensionUsingService {
private static final ExtensionPointName<MyBeanClass> EP_NAME =
ExtensionPointName.create("my.plugin.myExtensionPoint1");
public void useRegisteredExtensions() {
for (MyBeanClass extension : EP_NAME.getExtensionList()) {
String key = extension.getKey();
String clazz = extension.getClass();
// ...
}
}
}A gutter icon for the ExtensionPointName declaration allows navigating to the corresponding <extensionPoint> ("extensionPoint" in "Plugin Configuration File") declaration in plugin.xml. Code insight is available for the extension point name String literal (2022.3).
When processing extension implementations or registrations, there might be errors, compatibility and configuration issues. Use PluginException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/diagnostic/PluginException.java) to log and correctly attribute the causing plugin for builtin error reporting ("Error Reporting" in "IDE Infrastructure").
To report use of deprecated API, use PluginException.reportDeprecatedUsage() methods.
Examples:
CompositeFoldingBuilder.assertSameFile() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/folding/CompositeFoldingBuilder.java)
InspectionProfileEntry.getDisplayName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java)
To support Dynamic Plugins (Dynamic Plugins) (2020.1 and later), an extension point must adhere to specific usage rules:
extensions are enumerated on every use and extensions instances are not stored anywhere
alternatively, an ExtensionPointListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/extensions/ExtensionPointListener.kt) can perform necessary updates of data structures (register via ExtensionPointName.addExtensionPointListener())
Extension points matching these conditions can then be marked as dynamic by adding dynamic="true" in their declaration:
<extensionPoints>
<extensionPoint
name="myDynamicExtensionPoint"
beanClass="com.example.MyBeanClass"
dynamic="true"/>
</extensionPoints>All non-dynamic extension points are highlighted via Plugin DevKit | Plugin descriptor | Plugin.xml dynamic plugin verification inspection available in IntelliJ IDEA 2020.1 or later. Previous versions also highlight the dynamic attribute as "experimental".
Deprecation NoticeWhen writing new plugins, creating Components should be avoided. Any existing Components should be migrated to services, extensions, or listeners (see below).
Plugin Components are a legacy feature supported for compatibility with plugins created for older versions of the IntelliJ Platform. Plugins using Components do not support dynamic loading (Dynamic Plugins) (the ability to install, update, and uninstall plugins without restarting the IDE).
Plugin Components are defined in the <application-components>, <project-components>, and <module-components> sections in a Plugin Configuration File (Plugin Configuration File).
To migrate existing code from Components to more modern APIs, please see the following guidelines.
To manage some state or logic that is only needed when the user performs a specific operation, use a Service (Services).
To store the state of your plugin at the application or project level, use a Service (Services), and implement the PersistentStateComponent interface. See Persisting State of Components (Persisting State of Components) for details.
To subscribe to events, use a listener (Listeners) or create an extension (Extensions) for a dedicated extension point (for example, com.intellij.editorFactoryListener) if one exists for the event to subscribe to.
Executing code on application startup should be avoided whenever possible because it slows down startup.
Plugin code should only be executed when projects are opened (see Project Open) or when the user invokes an action of a plugin. If this cannot be avoided, add a listener (Listeners) subscribing to the AppLifecycleListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java) topic. See also Running Tasks Once ("Running Tasks Once" in "IDE Infrastructure").
Using Kotlin Coroutines, implement ProjectActivity (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) and register in com.intellij.postStartupActivity extension point.
Implementation in Kotlin (Configuring Kotlin Support) is required because Java doesn't support suspending functions.
To execute code when a project is being opened, use one of these two extensions (Extensions):
StartupActivity (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) for immediate execution on EDT. Implement DumbAware to indicate activity can run in background thread (in parallel with other such tasks).
StartupActivity.Background (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/startup/StartupActivity.kt) for execution with a 5-second delay in background thread (2019.3 or later).
Any long-running or CPU intensive tasks should be made visible to users by using ProgressManager.run(Task.Backgroundable). Access to indexes must be wrapped with DumbService ("Dumb Mode" in "Indexing and PSI Stubs"), see also General Threading Rules (General Threading Rules).
To execute code on project closing or application shutdown, implement the Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java) interface in a Service (Services) and place the code in the dispose() method. Alternatively, use Disposer.register() passing a Project or Application service instance as the parent argument (see Choosing a Disposable Parent ("Choosing a Disposable Parent" in "Disposer and Disposable")).
The plugin.xml configuration file contains all the information about the plugin, which is displayed in the plugins settings dialog (https://www.jetbrains.com/help/idea/managing-plugins.html), and all registered extensions, actions, listeners, etc. Sections below describe all the elements in detail.
The example plugin.xml files can be found in the IntelliJ SDK Docs Code Samples (https://github.com/JetBrains/intellij-sdk-code-samples) repository.
A plugin can contain additional configuration files beside the main plugin.xml. They have the same format, and they are included with the config-file attribute of <depends> elements specifying plugin dependencies (Plugin Dependencies). However, some elements and attributes required in plugin.xml are ignored in additional configuration files. If the requirements differ, the documentation below will state it explicitly. One use case for additional configuration files is when a plugin provides optional features that are only available in some IDEs and require certain modules ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products").
Please make sure to follow the guidelines from Plugin Overview page (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html) for an optimal presentation of your plugin on JetBrains Marketplace. The Busy Plugin Developers. Episode 2 discusses 5 tips for optimizing JetBrains Marketplace plugin page (https://youtu.be/oB1GA9JeeiY?t=52) in more detail.
See also Marketing about widgets and badges.
<idea-plugin>
<id>
<name>
<version>
<product-descriptor>
<idea-version>
<vendor>
<description>
<change-notes>
<depends>
<incompatible-with>
<actions>
<action>
<add-to-group>
<keyboard-shortcut>
<mouse-shortcut>
<override-text>
<synonym>
<abbreviation>
<group>
<action>
<add-to-group>
<keyboard-shortcut>
<mouse-shortcut>
<override-text>
<synonym>
<abbreviation>
<group>
<add-to-group>
<override-text>
<reference>
<add-to-group>
<separator>
<add-to-group>
<reference>
<add-to-group>
<separator>
<add-to-group>
<extensionPoints>
<extensionPoint>
<with>
<extensions>
<applicationListeners>
<listener>
<projectListeners>
<listener>
<resource-bundle>
Deprecated elements are omitted in the list above.
If an element or an attribute is not documented on this page, please consider them as configuration items intended to be used by JetBrains only. They should never be used by 3rd-party plugins.
The plugin.xml file root element.
yes
url (optional; ignored in additional configuration)
The link to the plugin homepage displayed on the plugin page in the JetBrains Marketplace (https://plugins.jetbrains.com).
require-restart (optional)
The boolean value determining whether the plugin installation, update, or uninstallation requires the IDE restart (see Dynamic Plugins for details).
Default value: false.
<actions>
<applicationListeners>
<change-notes>
<depends>
<incompatible-with>
<description>
<extensions>
<extensionPoints>
<id>
<idea-version>
<name>
<product-descriptor>
<projectListeners>
<resource-bundle>
<vendor>
<version>
Deprecated:
<application-components>
<module-components>
<project-components>
A unique identifier of the plugin. It should be a fully qualified name similar to Java packages and must not collide with the ID of existing plugins. The ID is a technical value used to identify the plugin in the IDE and JetBrains Marketplace (https://plugins.jetbrains.com). Please use characters, numbers, and '.'/'-'/'_' symbols only and keep it reasonably short.
Make sure to pick a stable ID, as the value cannot be changed between the plugin versions.
no; ignored in additional config file It is highly recommended to set in plugin.xml file. The element can be skipped in the source plugin.xml file if the Gradle patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task is enabled and configured.
Value of the <name> element.
<id>com.company.framework</id>The user-visible plugin display name (Title Case).
Reference: JetBrains Marketplace: Plugin Name (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name)
yes; ignored in additional config file
<name>My Framework Support</name>The plugin version displayed in the Plugins settings dialog and in the JetBrains Marketplace plugin page. Plugins uploaded to the JetBrains Marketplace must follow semantic versioning.
Reference: JetBrains Marketplace: Semantic Versioning (https://plugins.jetbrains.com/docs/marketplace/semver.html)
yes; ignored in additional config file The element can be skipped in the source plugin.xml file if the Gradle patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task is enabled and configured.
<version>1.3.18</version>Paid (https://plugins.jetbrains.com/build-and-market) or Freemium (https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin descriptor.
Reference: JetBrains Marketplace: How to add required parameters for paid plugins (https://plugins.jetbrains.com/docs/marketplace/add-required-parameters.html)
only for paid or freemium plugins; ignored in additional config file Do not add <product-descriptor> element in a free plugin.
code (required)
The plugin product code used in the JetBrains Sales System. The code must be agreed with JetBrains in advance and follow the requirements (https://plugins.jetbrains.com/docs/marketplace/obtain-a-product-code-from-jetbrains.html).
release-date (required)
Date of the major version release in the YYYYMMDD format.
release-version (required)
A major version in a special number format.
optional (optional)
The boolean value determining whether the plugin is a Freemium (https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin.
Default value: false.
The plugin's range of compatible IntelliJ-based IDE versions.
Reference: Build Number Ranges
yes; ignored in additional config file The element can be skipped in the source plugin.xml file if the Gradle patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task is enabled and configured.
since-build (required)
The lowest IDE version compatible with the plugin.
until-build (optional)
The highest IDE version compatible with the plugin. Undefined value declares compatibility with all the IDEs since the version specified by the since-build (also with the future builds what may cause incompatibility errors).
Compatibility with a specific build number (2021.3.3) and higher versions:
<idea-version since-build="213.7172.25"/>Compatibility with versions from any of 213 branches to any of 221 branches:
<idea-version
since-build="213" until-build="221.*"/>The vendor name or organization ID (if created) in the Plugins settings dialog and in the JetBrains Marketplace plugin page.
Reference: JetBrains Marketplace: Contacts and Resources (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#contacts-and-resources)
yes; ignored in additional config file
url (optional)
The link to the vendor's homepage.
email (optional)
The vendor's email address.
Personal vendor with an email address provided:
<vendor email="joe@example.com">Joe Doe</vendor>Organizational vendor with a website URL and email address provided:
<vendor
url="https://mycompany.example.com"
email="contact@example.com">
My Company
</vendor>The plugin description displayed on the JetBrains Marketplace plugin page and in the Plugins settings dialog.
Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed and must be wrapped into <![CDATA[... ]]> section.
Reference: JetBrains Marketplace: Plugin Description (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description)
yes; ignored in additional config file The element can be skipped in the source plugin.xml file if the Gradle patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task is enabled and configured.
<description><![CDATA[
Provides support for My Framework.
The support includes:
<ul>
<li>code completion</li>
<li>references</li>
</ul>
For more information visit the
<a href="https://example.com">project site</a>.
]]></description>A short summary of new features, bugfixes, and changes provided with the latest plugin version. Change notes are displayed on the JetBrains Marketplace plugin page and in the Plugins settings dialog.
Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed and must be wrapped into <![CDATA[... ]]> section.
Reference: JetBrains Marketplace: Change Notes (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#change-notes)
no; ignored in additional config file The element can be skipped in the source plugin.xml file if the Gradle patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") task is enabled and configured.
<change-notes><![CDATA[
<h2>New Features</h2>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li>Fixed issue 1</li>
<li>Fixed issue 2</li>
</ul>
]]></change-notes>Specifies a dependency on another plugin or a module of an IntelliJ Platform-based product. A single <idea-plugin> element can contain multiple <depends> elements.
References:
no; in most cases dependency on the platform ("Modules Available in All Products" in "Plugin Compatibility with IntelliJ Platform Products") module is needed
optional (optional)
Boolean value defining whether the dependency is optional to load the plugin in the IDE. If the dependency plugin is not installed in the current IDE, and optional is:
true - the plugin will be loaded
false (default) - the plugin will not be loaded
config-file (optional)
Relative path to an additional configuration file, loaded only if the dependency plugin is installed in the current IDE.
Required plugin dependency:
<depends>com.example.dependencypluginid</depends>Required dependency on the IntelliJ IDEA Java Module:
<depends>com.intellij.modules.java</depends>Optional plugin dependency:
<depends optional="true">
com.example.dependencypluginid
</depends>Required module dependency with additional configuration:
<depends
config-file="myPluginId-withJava.xml">
com.intellij.modules.java
</depends>Optional module dependency with additional configuration:
<depends
optional="true"
config-file="myPluginId-withKotlin.xml">
org.jetbrains.kotlin
</depends>Supported since 2020.2
Declares incompatibility with a provided module.
Reference: "Declaring Incompatibility with Module" in "Plugin Compatibility with IntelliJ Platform Products"
no; ignored in additional config file
<incompatible-with>
com.intellij.modules.appcode.ide
</incompatible-with>A resource bundle to be used with message key attributes in extension declarations and for action and group localization ("Localizing Actions and Groups" in "Actions"). A single <idea-plugin> element can contain multiple <resource-bundle> elements.
no
To load the content of messages/Bundle.properties bundle, declare:
<resource-bundle>messages.Bundle</resource-bundle>Defines the application-level listeners.
Reference: "Defining Application-Level Listeners" in "Listeners"
no
<listener>
Defines the project-level listeners.
Reference: "Defining Project-Level Listeners" in "Listeners"
no
<listener>
Defines a single application or project-level listener. A single <applicationListeners> or <projectListeners> can contain multiple <listener> elements.
Reference: Listeners
no
topic (required)
The fully qualified name of the listener interface corresponding to the type of received events.
class (required)
The fully qualified name of the class implementing the listener interface that receives and handles the events.
os (optional; supported since 2020.1)
Restricts listener instantiation to a specific operating system. Allowed values:
freebsd
mac
linux
unix
windows
activeInTestMode (optional)
Boolean flag defining whether the listener should be instantiated in test mode.
Default value: true.
activeInHeadlessMode (optional)
Boolean flag defining whether the listener should be instantiated in headless mode.
Default value: true.
<listener
topic="com.intellij.ide.AppLifecycleListener"
class="com.example.MyListener"
os="mac"
activeInTestMode="false"/>Defines the plugin actions.
Reference: Actions
no
resource-bundle (optional; supported in 2020.1+)
Defines the dedicated actions resource bundle. See "Localizing Actions and Groups" in "Actions" for more details.
<action>
<group>
<reference>
<separator>
<actions resource-bundle="messages.ActionsBundle">
<!--
Actions/Groups defined here will use keys
from the ActionsBundle.properties bundle.
-->
</actions>A single action entry of the <actions> implemented by the plugin. A single <actions> element can contain multiple <action> elements.
Reference: "Registering Actions in plugin.xml" in "Actions"
no
id (required)
A unique action identifier. The action identifier must be unique between different plugins. Thus, it is recommended to prepend it with the value of the plugin <id>.
class (required)
The fully qualified name of the action implementation class.
text (required if the action is not localized ("Localizing Actions and Groups" in "Actions"))
The default long-version text to be displayed for the action (tooltip for toolbar button or text for menu item).
description (optional)
The text which is displayed in the status bar when the action is focused.
icon (optional)
The icon that is displayed on the toolbar button or next to the action menu item. See Working with Icons for more information about defining and using icons.
use-shortcut-of (optional)
The ID of the action whose keyboard shortcut this action will use.
<abbreviation>
<add-to-group>
<keyboard-shortcut>
<mouse-shortcut>
<override-text>
<synonym>
Action declaring explicit text:
<action
id="com.example.myframeworksupport.MyAction"
class="com.example.impl.MyAction"
text="Do Action"
description="Do something with the code"
icon="AllIcons.Actions.GC">
<!-- action children elements -->
</action>Action without the text attribute must use the texts from the resource bundle declared with the <resource-bundle> element, or the resource-bundle attribute of the <actions> element:
<action
id="com.example.myframeworksupport.MyAction"
class="com.example.impl.MyAction"
icon="AllIcons.Actions.GC"/>Specifies that the action should be added to an existing group. A single action can be added to multiple groups.
no
group-id (required)
Specifies the ID of the group to which the action is added. The group must be an implementation of the DefaultActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) class.
anchor (optional)
Specifies the position of the action in the relative to other actions. Allowed values:
first - the action is placed as the first in the group
last (default) - the action is placed as the last in the group
before - the action is placed before the action specified by the relative-to-action attribute
after - the action is placed after the action specified by the relative-to-action attribute
relative-to-action (required if anchor is before/after)
The action before or after which the current action is inserted.
<add-to-group
group-id="ToolsMenu"
anchor="after"
relative-to-action="GenerateJavadoc"/>Specifies the keyboard shortcut for the action. A single action can have several keyboard shortcuts.
no
keymap (required)
Specifies the keymap for which the action shortcut is active. IDs of the standard keymaps are defined as constants in the KeymapManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/keymap/KeymapManager.java) class.
first-keystroke (required)
Specifies the first keystroke of the action shortcut. The keystrokes are specified according to the regular Swing rules.
second-keystroke (optional)
Specifies the second keystroke of the action shortcut.
remove (optional)
Removes a shortcut from the specified action.
replace-all (optional)
Removes all keyboard and mouse shortcuts from the specified action before adding the specified shortcut.
Add the first and second keystrokes to all keymaps:
<keyboard-shortcut
keymap="$default"
first-keystroke="control alt G"
second-keystroke="C"/>Remove the given shortcut from the Mac OS X keymap:
<keyboard-shortcut
keymap="Mac OS X"
first-keystroke="control alt G"
second-keystroke="C"
remove="true"/>Remove all existing keyboard and mouse shortcuts and register one for the Mac OS X 10.5+ keymap only:
<keyboard-shortcut
keymap="Mac OS X 10.5+"
first-keystroke="control alt G"
second-keystroke="C"
replace-all="true"/>Specifies the mouse shortcut for the action. A single action can have several mouse shortcuts.
no
keymap (required)
Specifies the keymap for which the action shortcut is active. IDs of the standard keymaps are defined as constants in the KeymapManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/keymap/KeymapManager.java) class.
keystroke (required)
Specifies the clicks and modifiers for the action. It is defined as a sequence of words separated by spaces:
modifier keys: shift, control, meta, alt, altGraph
mouse buttons: button1, button2, button3
button double-click: doubleClick
remove (optional)
Removes a shortcut from the specified action.
replace-all (optional)
Removes all keyboard and mouse shortcuts from the specified action before adding the specified shortcut.
Add the shortcut to all keymaps:
<mouse-shortcut
keymap="$default"
keystroke="control button3 doubleClick"/>Remove the given shortcut from the Mac OS X keymap:
<mouse-shortcut
keymap="Mac OS X"
keystroke="control button3 doubleClick"
remove="true"/>Remove all existing keyboard and mouse shortcuts and register one for the Mac OS X 10.5+ keymap only:
<mouse-shortcut
keymap="Mac OS X 10.5+"
keystroke="control button3 doubleClick"
replace-all="true"/>Defines an alternate version of the text for the menu action or group.
2020.1+ for actions 2020.3+ for groups
no
place (required)
Declares where the alternate text should be used.
text (text or use-text-of-place is required)
Defines the text to be displayed for the action.
use-text-of-place (text or use-text-of-place is required)
Defines a location whose text should be displayed for this action.
Explicitly overridden text:
<override-text
place="MainMenu"
text="Collect _Garbage"/>Overridden text reused from the MainMenu place:
<override-text
place="EditorPopup"
use-text-of-place="MainMenu"/>Defines an alternative text for searching the action in Help | Find Action... or Navigate | Search Everywhere popups. A single action can have multiple synonyms.
2020.3+
no
key (key or text is required)
The key of synonym text provided in a message bundle.
text (key or text is required)
The synonym text.
<!-- Default action text: Delete Element -->
<synonym key="my.action.text.remove.element"/>
<synonym text="Remove Element"/>Defines an abbreviation for searching the action in Help | Find Action... or Navigate | Search Everywhere popups. A single action can have multiple abbreviations.
no
value (required)
The abbreviation value.
<!-- Default action text: UI Inspector -->
<abbreviation value="uii"/>Defines an action group. The <action>, <group> and <separator> elements defined inside the group are automatically included in it. The <group> elements can be nested.
Reference: "Grouping Actions" in "Actions"
no
id (required)
A unique group identifier. The group identifier must be unique between different plugins. Thus, it is recommended to prepend it with the value of the plugin <id>.
class (optional)
The fully qualified name of the group implementation class. If not specified, DefaultActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) is used.
text (required if the popup is true and group is not localized ("Localizing Actions and Groups" in "Actions"))
The default long-version text to be displayed for the group (text for the menu item showing the submenu).
description (optional)
The text which is displayed in the status bar when the group is focused.
icon (optional)
The icon that is displayed next to the group menu item. See Working with Icons for more information about defining and using icons.
popup (optional)
Boolean flag defining whether the group items are presented in the submenu popup.
true - group actions are placed in a submenu
false (default) - actions are displayed as a section of the same menu delimited by separators
compact (optional)
Boolean flag defining whether disabled actions within this group are hidden. If the value is:
true - disabled actions are hidden
false (default) - disabled actions are visible
use-shortcut-of (optional)
The ID of the action whose keyboard shortcut this group will use.
searchable (optional; supported in 2020.3+)
Boolean flag defining whether the group is displayed in Help | Find Action... or Navigate | Search Everywhere popups.
Default value: true.
<action>
<add-to-group>
<group>
<override-text>
<reference>
<separator>
Group declaring explicit text:
<group
id="com.example.myframeworksupport.MyGroup"
popup="true"
text="My Tools">
<!-- group children elements -->
</group>A popup group without the text attribute must use the texts from the resource bundle declared with the <resource-bundle> element, or the resource-bundle attribute of the <actions> element:
<group
id="com.example.myframeworksupport.MyGroup"
popup="true"/>A group with custom implementation and icon:
<group
id="com.example.myframeworksupport.MyGroup"
class="com.example.impl.MyGroup"
icon="AllIcons.Actions.GC"/>Allows adding an existing action to the group. The element can be used directly under the <actions> element, or in the <group> element.
no
ref (required)
The ID of the action to add to a group.
<add-to-group>
An action reference in a group:
<group ...>
<reference ref="EditorCopy"/>
</group>An action reference registered directly in the <actions> element:
<actions>
<reference ref="com.example.MyAction">
<add-to-group group-id="ToolsMenu"/>
</reference>
</group>Defines a separator between actions in a group. The element can be used directly under the <actions> element with the child <add-to-group> element defining the target group, or in the <group> element.
no
text (optional)
Text displayed on the separator. Separator text is displayed only in specific contexts such as popup menus, toolbars, etc.
key (optional)
The message key for the separator text. The message bundle for use should be registered via the resource-bundle attribute of the <actions> element. The attribute is ignored if the text attribute is specified.
<add-to-group>
A separator dividing two actions in a group:
<group ...>
<action .../>
<separator/>
<action .../>
</group>A separator registered directly in the <actions> element:
<actions>
<separator>
<add-to-group
group-id="com.example.MyGroup"
anchor="first"/>
</separator>
</group>A separator with a defined text:
<separator text="Group By"/>A separator with a text defined by message key:
<separator key="message.key"/>Defines the plugin extensions.
Reference: Extensions
no
defaultExtensionNs (optional)
Default extensions namespace. It allows skipping the common prefix in fully qualified extension point names. Usually, the com.intellij namespace is used when the plugin implements IntelliJ Platform extensions.
The children elements are registrations of the extension points defined by <extensionPoint> elements. Extension elements names follow the EPs names defined by name or qualifiedName attributes.
Extensions declaration with the default namespace:
<extensions defaultExtensionNs="com.intellij">
<applicationService
serviceImplementation="com.example.Service"/>
</extensions>Extensions declaration using the fully qualified extension name:
<extensions>
<com.example.vcs.myExtension
implementation="com.example.MyExtension"/>
</extensions>Extension points defined by the plugin.
Reference: Extension Points
no
<extensionPoint>
A single extension point entry of the <extensionPoints> defined by the plugin. A single <extensionPoints> element can contain multiple <extensionPoint> elements.
Reference: "Declaring Extension Points" in "Extension Points"
no
name (name or qualifiedName is required)
The extension point name that should be unique in the scope of the plugin, e.g., myExtension. The fully qualified name of the extension point is built at runtime by prepending the value of the name attribute with the plugin <id> + . prefix. Only one of the name and qualifiedName attributes can be specified.
Example: when the name is myExtension and plugin ID is com.example.myplugin, the fully qualified name of the EP will be com.example.myplugin.myExtension.
qualifiedName (name or qualifiedName is required)
The fully qualified name of the extension point. It should be unique between different plugins, and it is recommended to include a plugin ID to guarantee uniqueness, e.g., com.example.myplugin.myExtension. Only one of the name and qualifiedName attributes can be specified.
interface (interface or beanClass is required)
The fully qualified name of the interface to be implemented for extending plugin's functionality. Only one of the interface and beanClass attributes can be specified. See Extension Points for more information.
beanClass (interface or beanClass is required)
The fully qualified name of the extension point bean class providing additional information to the plugin. Only one of the interface and beanClass attributes can be specified. See Extension Points for more information.
dynamic (optional)
Boolean value defining whether the extension point meets the requirements to be dynamic ("Dynamic Extension Points" in "Extension Points"), which is a prerequisite for dynamic plugins (Dynamic Plugins). Default value: false.
area (optional)
The scope in which the extension (Extensions) is instantiated. It is not recommended to use non-default values. Allowed values:
IDEA_APPLICATION (default)
IDEA_PROJECT
IDEA_MODULE (deprecated)
<with>
Specifies the required parent type for class names provided in extension point tags or attributes. A single <extensionPoint> element can contain multiple <with> elements.
no
tag (tag or attribute is required)
The name of the tag holding the fully qualified name of the class which parent type will be limited by the type provided in the implements attribute. Only one of the tag and attribute attributes can be specified.
attribute (tag or attribute is required)
The name of the attribute holding the fully qualified name of the class which parent type will be limited by the type provided in the implements attribute. Only one of the tag and attribute attributes can be specified.
implements (required)
The fully qualified name of the parent type limiting the type provided in the place specified by tag or attribute.
An extension point which restricts the type provided in a myClass attribute to be an instance of com.example.ParentType, and the type provided in a someClass element to be an instance of java.lang.Comparable:
<extensionPoint
name="myExtension"
beanClass="com.example.MyExtension">
<with
attribute="myClass"
implements="com.example.ParentType"/>
<with
tag="someClass"
implements="java.lang.Comparable"/>
</extensionPoint>When using the above extension point, an implementation could be registered as follows:
<myExtension ...
myClass="com.example.MyCustomType">
<someClass>com.example.MyComparable</someClass>
</myExtension>where:
com.example.MyCustomType must be a subtype of com.example.ParentType
com.example.MyComparable must be a subtype of java.lang.Comparable
Element is deprecated. Do not use it in new plugins.
Defines a list of application components (Components).
no
<component>
Element is deprecated. Do not use it in new plugins.
Defines a list of project components (Components).
no
<component>
Element is deprecated. Do not use it in new plugins.
Defines a list of module components (Components).
no
<component>
Element is deprecated. Do not use it in new plugins.
Defines a single application, project, or module component (Components). A single <application-components>, <project-components>, or <module-components> element can contain multiple <component> elements.
no
<headless-implementation-class>
<implementation-class>
<interface-class>
<loadForDefaultProject>
<option>
Element is deprecated. Do not use it in new plugins.
The fully qualified name of the component implementation class.
yes
Element is deprecated. Do not use it in new plugins.
The fully qualified name of the component interface class. If not specified, the interface will be the same as defined by <implementation-class> element.
no
Element is deprecated. Do not use it in new plugins.
The fully qualified name of the component implementation class to be used when the IDE runs in headless mode.
no
Element is deprecated. Do not use it in new plugins.
Allows to provide additional component options. A single <component> element can contain multiple <option> elements.
no
name (required)
Option name.
value (required)
Option value.
Element is deprecated. Do not use it in new plugins.
If present, the component is instantiated also for the default project. It takes effect only when used inside of <project-components> element.
no
Beginning in version 2019.1, the IntelliJ Platform supports representing a plugin with a logo. A Plugin Logo is intended to be a unique representation of a plugin's functionality, technology, or company.
Note: icons and images used within a plugin have different requirements. See Working with Icons for more information.
Plugin Logos are shown in the JetBrains Marketplace (https://plugins.jetbrains.com). They also appear in the Settings Plugin Manager (https://www.jetbrains.com/help/idea/managing-plugins.html) UI in IntelliJ Platform-based IDEs. Whether online or in the product UI, a Plugin Logo helps users to identify a plugin more quickly in a list, as shown below:

When browsing custom plugin repositories (Custom Plugin Repository), there is no support for showing logos for plugins hosted there but not yet installed.
Please see also these important requirements (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-logo) for JetBrains Marketplace.
For a Plugin Logo to be displayed correctly within an IntelliJ Platform-based IDE, it must:
Follow the best practices design guidelines,
Be in the correct file format,
Conform to file name conventions,
Have the correct size,
Be in the META-INF folder of the plugin distribution file.
The Plugin Logo should be provided in one size: 40px by 40px.
A Plugin Logo is displayed in two sizes, and scales automatically in each context:
40px by 40px in the plugins list in the Plugin Manager UI.
80px by 80px in the plugin details screen in the Plugin Manager UI and on the plugin's page in JetBrains Marketplace.
Verify that Plugin Logo designs are effective in both sizes and all display contexts.
Plugin Logo designs should leave at least 2px transparent padding around the perimeter, as shown below:

Make sure Plugin Logos have the same visual weight as the logos in the examples below. The more filled a Plugin Logo design is, the less actual space it needs. See more examples of visual weight compensation (https://jetbrains.design/intellij/principles/icons/#08) in the IntelliJ Platform UI Guidelines for Icons.
For basic shapes, use the following sizes. Note the different areas of transparent padding used for each shape:
![]() | ![]() |
Square logo 32px by 32px | Circular logo 36px in diameter |
![]() | ![]() |
Horizontal rectangular logo 36px by 26px | Vertical rectangular logo 26px by 36px |
If the plugin's technology already has a logo, use its colors. Check the license terms before using the logo. If there is no existing logo, or its use is prohibited, create a custom logo based on the Action Colors Palette (https://jetbrains.design/intellij/principles/icons/#action-icons) in the IntelliJ Platform UI Guidelines for Icons.
![]() | ![]() |
The YouTrack Plugin Logo uses the YouTrack product logo | The Keymap Plugin Logo uses a color from the Action Colors Palette |
Ensure a Plugin Logo is visible on both light and dark backgrounds. If one Plugin Logo design does not work on both light and dark backgrounds, create separate light and dark versions of the Plugin Logo. The examples below illustrate how a Plugin Logo design may work well for a light background but not for a dark background. Consequently, a separate Plugin Logo for dark backgrounds is needed.
![]() | ![]() | ![]() |
The light Plugin Logo design works well on light theme | The light Plugin Logo design does not work well on a dark theme | A separate, dark Plugin Logo design works well on dark theme |
All Plugin Logo images must be in SVG format. This vector image format is required because the Plugin Logo file must be small (ideally less than 2-3kB), and the image must scale without any loss of quality.
Using automatic conversion of bitmap graphics to SVG is highly discouraged, as the resulting files have excessive size (100kB or more).
Name the Plugin Logo files according to the following conventions:
pluginIcon.svg is the default Plugin Logo. If a separate Logo file for dark themes exists in the plugin, then this file is used solely for light themes,
pluginIcon_dark.svg is an optional, alternative Plugin Logo for use solely with dark IDE themes.
The Plugin Logo files must be in the META-INF folder of the plugin distribution file, i.e., the plugin.jar or plugin.zip file you upload to the plugin repository and install into an IDE.
To include Plugin Logo files in your distribution file, place the Plugin Logo files into a plugin project's resources/META-INF folder. For example:

A plugin may depend on API and classes from other plugins, either bundled or third-party.
This document describes the syntax for declaring plugin dependencies and optional plugin dependencies. For more information about dependencies on the IntelliJ Platform modules, see Plugin Compatibility with IntelliJ Platform Products.
For adding dependencies on 3rd party libraries, use regular Gradle dependency management (https://docs.gradle.org/current/userguide/core_dependency_management.html).
To express a dependency on classes from other plugins or modules, perform the following three required steps detailed below on this page:
Locate Plugin ID
Project Setup
Declaration in plugin.xml (Plugin Configuration File)
Getting java.lang.NoClassDefFoundErrorIf java.lang.NoClassDefFoundError occurs at runtime, most likely Step 3 was omitted.
Otherwise, loading the plugin dependency may have failed, please check log files from the Development Instance ("Development Instance Settings, Caches, Logs, and Plugins" in "IDE Development Instance")).
A compatible version must be chosen carefully according to the plugin's compatibility (Build Number Ranges). For non-bundled plugins, it is not possible to specify the minimum/maximum version for the dependent plugin. (Issue (https://youtrack.jetbrains.com/issue/IDEABKL-7906))
For plugins published on JetBrains Marketplace (https://plugins.jetbrains.com):
Open plugin's detail page
Scroll down to the bottom section Additional Information
Copy Plugin ID
When using Gradle IntelliJ Plugin (Developing a Plugin), all bundled plugin IDs can be gathered using listBundledPlugins ("listBundledPlugins" in "Gradle IntelliJ Plugin") task.
When using DevKit (Developing a Theme) and for non-public plugins, locate the plugin's main JAR file containing META-INF/plugin.xml descriptor with <id> ("id" in "Plugin Configuration File") tag (or <name> ("name" in "Plugin Configuration File") if not specified). Bundled plugins are located in $PRODUCT_ROOT$/plugins/$PLUGIN_NAME$/lib/$PLUGIN_NAME$.jar.
The following table lists some commonly used bundled plugins and their ID. See also IntelliJ Community Plugins Extension Point and Listener List and "Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products".
Plugin Name | Plugin ID | Related Documentation |
|---|---|---|
Copyright | com.intellij.copyright | |
CSS | com.intellij.css | |
Database Tools and SQL | com.intellij.database | |
IntelliLang | org.intellij.intelliLang | |
Java | com.intellij.java | |
JavaScript and TypeScript | JavaScript | |
Kotlin | org.jetbrains.kotlin | |
Markdown | org.intellij.plugins.markdown | |
Maven | org.jetbrains.idea.maven | |
Spring | com.intellij.spring | |
Spring Boot | com.intellij.spring.boot | |
YAML | org.jetbrains.plugins.yaml |
If the plugin is not bundled with the target IDE, run the (sandbox) IDE Development Instance (IDE Development Instance) of your target IDE and install the plugin there.
Depending on the chosen development workflow (Gradle or DevKit), one of the two following steps is necessary.
Please see the intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") property for acceptable values.
Add the dependency to the intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") parameter in your build script:
intellij {
plugins.set(listOf("com.example.another-plugin:1.0"))
}intellij {
plugins = ['com.example.another-plugin:1.0']
}Transitive dependencies required for tests must currently be specified explicitly (https://github.com/JetBrains/gradle-intellij-plugin/issues/38).
Existing DevKit-based projects can be converted to use Gradle setup (Migrating DevKit Plugin to Gradle) where dependency management is fully automated.
Add the JARs of the plugin on which the project depends to the Classpath of the IntelliJ Platform SDK ("Add IntelliJ Platform Plugin SDK" in "Setting Up a Development Environment").
Do not add the plugin JARs as a library: this will fail at runtime because the IntelliJ Platform will load two separate copies of the dependency plugin classes.
Open the Project Structure dialog and go to Platform Settings | SDKs section.
Select the SDK used in the project.
Click the + button in the Classpath tab.
Select the plugin JAR depending on whether it is bundled or non-bundled plugin:
For bundled plugins, the plugin JAR files are located in plugins/$PLUGIN_NAME$ or plugins/$PLUGIN_NAME$/lib under the main installation directory.
For non-bundled plugins, depending on the platform version, the plugin JAR files are located in:
plugins directory for versions 2020.1+ (https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#plugins-directory)
plugins directory for versions pre-2020.1 (https://www.jetbrains.com/help/idea/2019.3/tuning-the-ide.html#plugins-directory)
Regardless of whether a plugin project uses "Modules Available in All Products" in "Plugin Compatibility with IntelliJ Platform Products", or "Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products", the correct module must be listed as a dependency in plugin.xml. If a project depends on another plugin, the dependency must be declared like a module ("Modules" in "Plugin Compatibility with IntelliJ Platform Products"). If only general IntelliJ Platform features (APIs) are used, then a default dependency on com.intellij.modules.platform must be declared.
To display a list of available IntelliJ Platform modules, invoke the code completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#4eac28ba) feature for the <depends> ("depends" in "Plugin Configuration File") element contents while editing the plugin project's plugin.xml file.
In the plugin.xml, add a <depends> tag with the dependency plugin's ID as its content. Continuing with the example from Project Setup above, the dependency declaration in plugin.xml would be:
<depends>com.example.another-plugin</depends>A plugin can also specify an optional plugin dependency. In this case, the plugin will load even if the plugin it depends on is not installed or enabled, but part of the plugin's functionality will not be available.
Declare additional optional="true" and required config-file attribute pointing to the optional plugin descriptor file ("Additional Plugin Configuration Files" in "Plugin Configuration File"):
<depends
optional="true"
config-file="myPluginId-optionalPluginName.xml">dependency.plugin.id</depends>Additional plugin descriptor files must follow the naming pattern myPluginId-$NAME$.xml resulting in unique filenames to prevent problems with classloaders in tests (Details (https://youtrack.jetbrains.com/issue/IDEA-205964)).
The plugin adds additional highlighting for Java and Kotlin files. The main plugin.xml defines a required dependency on the Java plugin (plugin ID com.intellij.java) and registers the corresponding com.intellij.annotator extension. Additionally, it specifies an optional dependency on the Kotlin plugin (plugin ID org.jetbrains.kotlin):
plugin.xml
<idea-plugin>
...
<depends>com.intellij.java</depends>
<depends
optional="true"
config-file="myPluginId-withKotlin.xml">org.jetbrains.kotlin</depends>
<extensions defaultExtensionNs="com.intellij">
<annotator
language="JAVA"
implementationClass="com.example.MyJavaAnnotator"/>
</extensions>
</idea-plugin>The configuration file myPluginId-withKotlin.xml is located in the same directory as the main plugin.xml file. In that file, the annotator extension for Kotlin is defined:
myPluginId-withKotlin.xml
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<annotator
language="kotlin"
implementationClass="com.example.MyKotlinAnnotator"/>
</extensions>
</idea-plugin>Starting with the 2020.1 release, installing, updating, and uninstalling plugins without restarting the IDE is available in the IntelliJ Platform.
During plugin development, Auto-Reload ("Enabling Auto-Reload" in "IDE Development Instance") also allows code changes to take effect immediately in the sandbox IDE instance. To test whether dynamic installation works correctly, verify installing local build distribution ("Building Distribution" in "Publishing a Plugin") succeeds (see Troubleshooting).
Please note that any unloading problems in a production environment will simply ask the user to restart the IDE.
If a plugin requires restart (e.g., due to using native libraries) specify require-restart="true" for <idea-plugin> ("idea-plugin" in "Plugin Configuration File") root tag in plugin.xml (Plugin Configuration File).
3rd-party paid plugins cannot be installed, updated or uninstalled without restarting the IDE.
For a plugin to support this, all restrictions listed below must be met. To verify a plugin locally, invoke Code | Analyze Code | Run Inspection by Name... and run Plugin DevKit | Plugin descriptor | Plugin.xml dynamic plugin verification inspection inspection on all plugin descriptor files.
For plugins hosted on the JetBrains Marketplace (https://plugins.jetbrains.com) the built-in Plugin Verifier (https://blog.jetbrains.com/platform/2018/07/plugins-repository-now-integrates-with-the-plugin-verification-tool/) will run these checks automatically. See "Plugin Verifier" in "Verifying Plugin Compatibility" for more information on how to run it locally or on CI.
No Components must be used; existing ones must be migrated (Components) to services, extensions, or listeners.
All <group> ("group" in "Plugin Configuration File") elements must declare a unique id.
Whether defined in the platform itself (IntelliJ Platform Extension Point and Listener List) or coming from other plugins, all used extension points must be marked explicitly as dynamic (see next paragraph).
Some deprecated extension points (e.g., com.intellij.configurationProducer) are intentionally non-dynamic, and their usage should be migrated to the corresponding replacement.
If a plugin defines its own custom extension points, they must adhere to specific usage rules and then be declared ("Dynamic Extension Points" in "Extension Points") ready for dynamic use explicitly.
Any Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) which depends on dynamic extension points must implement Configurable.WithEpDependencies.
Application, project, and module services (Services) declared with overrides="true" are not allowed.
Loading and unloading plugins happens in EDT and under write action.
Loading/Unloading a plugin clears all cached values created using CachedValuesManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java).
Do not store references to PSI elements in objects which can survive plugin loading or unloading; use SmartPsiElementPointer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/SmartPsiElementPointer.java) instead.
Replace with String from Language.getID()/FileType.getName() (use inspection Plugin DevKit | Code | Map key may leak).
Register DynamicPluginListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/plugins/DynamicPluginListener.kt) application listener (Listeners) to receive updates on plugin load/unload events.
This can be used to e.g., cancel long-running activities or disallow unload due to ongoing processes.
Use Services implementing Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java) and perform cleanup in Disposable.dispose().
When a plugin is being uninstalled or updated, the IDE waits synchronously for plugin unload and asks for restart if the unloading failed.
If it fails:
Try a newer version of the IDE (eventually latest available from Early Access Program (https://eap.jetbrains.com)), in some cases platform bugs might be an issue. See this list of known platform issues (https://youtrack.jetbrains.com/issues/IDEA?q=%23dynamic-plugins%20) related to handling dynamic plugins.
Try in a fresh and new configuration (e.g., clean the sandbox ("The Development Instance Sandbox Directory" in "IDE Development Instance") or use a different configuration directory).
All events are tracked under com.intellij.ide.plugins.DynamicPlugins category in IDE log file. If a plugin fails to reload, the log will contain a cause as to why.
Verify that the IDE is running with the VM parameter -XX:+UnlockDiagnosticVMOptions. When using Gradle (Configuring Gradle IntelliJ Plugin), specify runIde.jvmArgs += "-XX:+UnlockDiagnosticVMOptions" otherwise Configure JVM Options (https://www.jetbrains.com/help/idea/tuning-the-ide.html#procedure-jvm-options).
Set Registry key ide.plugins.snapshot.on.unload.fail to true (Go to Navigate | Search Everywhere and type Registry).
Trigger the plugin reload.
Open the .hprof memory snapshot generated in user home directory on plugin unload, look for the plugin ID string. IntelliJ Ultimate (https://www.jetbrains.com/help/idea/analyze-hprof-memory-snapshots.html) can open memory snapshots directly.
Find the PluginClassLoader referencing the plugin ID string
Look at references to the PluginClassLoader instance.
Every one of them is a memory leak (or part of a memory leak) that needs to be resolved.
When you've completed step 1 and 2, the log will contain more information about the memory leak, for instance the following shows a chain of field references that is keeping the class loader in memory.
2020-12-26 14:43:24,563 [ 251086] INFO - lij.ide.plugins.DynamicPlugins - Snapshot analysis result: Root 1:
ROOT: Global JNI
sun.awt.X11.XInputMethod.clientComponentWindow
com.intellij.openapi.wm.impl.IdeFrameImpl.rootPane
com.intellij.openapi.wm.impl.IdeRootPane.myToolbar
com.intellij.openapi.actionSystem.impl.ActionToolbarImpl.myVisibleActions
java.util.ArrayList.elementData
java.lang.Object[]
com.example.ActionExample.<class>
com.example.ActionExample.<loader>
* com.intellij.ide.plugins.cl.PluginClassLoaderUser Experience (UX) is a term describing the experience users feel when using a product. It covers not only aspects like visual presentation, performance, stability, or ease of use, but anything that affects the user's satisfaction. It's an overall impression of using a product that strongly indicates whether a user likes it or not.
IntelliJ Platform-based plugins are products, and providing a good UX is very important for retaining users and making them recommend your plugin to others. A bad plugin UX can negatively impact the user base, as users tend to abandon plugins that bring frustration, e.g., by instability, poor performance, or complexity.
The following sections explain the selected plugin UX areas.
Great IntelliJ Platform plugins, like any other products, should bring significant value to users. When planning your work, talk and try to understand what your users need the most and prioritize crucial functionalities.
If you are unsure about an implemented solution, consider sharing the work in progress with a limited group of users (e.g., your colleagues or active community members) and gathering the feedback that will help you improve the final result. See the Custom Release Channels ("Specifying a Release Channel" in "Publishing a Plugin") section for information on how to automate sharing the pre-release plugin versions.
Gathering feedback on the existing features can help identify what works well and what should be improved, which also increases the overall plugin quality.
Plugins should be just easy to use. Ideally, all the features should work out of the box after the installation, without any special user interactions, like manually enabling crucial plugin features. Default settings should reflect the typical plugin usage in a standard project.
All the settings and actions should be easy to find and be placed in the proper settings (Settings) or action group (Grouping Actions), e.g.:
Framework plugin settings should be placed under the Settings | Languages & Frameworks
Action marking a directory as a plugin-specific root type should be added to Mark Directory as... group
Plugins that are hard to configure with features that are hard to find may be quickly abandoned out of frustration.
Plugins that throw a lot of errors visible in the messages panel and execute actions resulting in incorrect behavior, e.g., generating incorrect code, are considered unstable and unreliable.
To improve the overall stability and minimize the risk of introducing regression issues, it is critical to implement functional tests (Testing Overview) with a low maintenance cost. It is a great safety net, which in the long term, will speed up your development and help you release new versions without the fear of breaking existing plugin features.
It's nearly impossible to develop software without bugs, so it is recommended to set up an issue tracker where users can report errors. In addition, it's worth implementing error reporting ("Error Reporting" in "IDE Infrastructure") that allows sending reports directly from within the IDE, with all required information attached. To reproduce and understand problems in production, use logging ("Logging" in "IDE Infrastructure") consistently.
Even if a plugin works correctly and looks pleasing, poor performance will impact user satisfaction. Slow highlighting, code completion, code generation, and other features may break the user's workflow and cause frustration leading to plugin uninstallation. Always try to follow the performance tips described on the relevant topic pages, e.g., PSI Performance, "Avoiding UI Freezes" in "General Threading Rules", "Improving Indexing Performance" in "Indexing and PSI Stubs".
Making as much functionality as possible working during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") can also improve perceived performance.
In addition to the plugin execution performance, it is recommended to optimize the size of the plugin distribution that is downloaded by users from JetBrains Marketplace. Users with a poor internet connection may cancel the download when they realize that it will take too long to wait for a plugin they are not sure will meet their expectations. Consider the following techniques for optimizing the plugin distribution size:
Decrease the number of dependencies. Check if the target platform includes utilities (like those mentioned in User Interface FAQ) or libraries ("Bundled Library Updates" in "Incompatible Changes in IntelliJ Platform and Plugins API") you need and reuse them.
Optimize assets like icons, images, videos, etc.
If large resources (e.g., SDKs) are needed only in specific setups, consider downloading them by the plugin on-demand instead of bundling them in the plugin distribution.
Obfuscation (https://plugins.jetbrains.com/docs/marketplace/obfuscate-the-plugin.html) may also help reduce the distribution file size.
When designing and implementing features, review existing functionalities of the IDE and plugins and design your features in a similar and consistent way. A consistent approach will make it easier for users to work with your plugin, increasing overall satisfaction.
If you implement custom language support (Custom Language Support), review and consider implementing extension points from the Additional Minor Features list so that your plugin provides an experience consistent with other plugins.
If a plugin UI significantly differs from the other application parts, it can feel alien to people and might be considered low quality or neglected. Review and follow the rules described in the IntelliJ Platform UI Guidelines (https://jetbrains.design/intellij/). Pay attention to icons (https://jetbrains.design/intellij/principles/icons/) and make them match the IDE style. Use the UI controls recommended for a given use case. Use UI Inspector (Internal Actions - UI Inspector) to see how the existing UI is implemented.
Bad-quality labels or texts with typos and grammatical errors make a bad impression. All the UI texts should follow the rules described in the Text section of the IntelliJ Platform UI Guidelines (https://jetbrains.design/intellij/). It is recommended to proofread the texts or use one of the spellcheck plugins (https://plugins.jetbrains.com/search?tags=Spellcheck).
The description on the plugin page is the place where users have the first contact with the plugin, and its content helps them to decide if it is what they are looking for. The description should be clear, polished, and follow the rules described in the JetBrains Marketplace documentation (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description). See also this webinar about 5 tips for optimizing the JetBrains Marketplace plugin page (https://youtu.be/oB1GA9JeeiY?t=52).
This FAQ is a topical index of questions that have been asked (and answered) on our IntelliJ IDEA Open API and Plugin Development forum (https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development).
See also Explore the IntelliJ Platform API (Explore the IntelliJ Platform API) for more information and strategies.
See also Program Structure Interface (PSI) and PSI Cookbook
How do I find all subclasses of a class? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791895-finding-all-derived-class-given-parent-class)
How do I find all anonymous classes created in a class? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206792205-How-to-find-anonymous-classes-in-PsiClass-)
How do I calculate the value of a string literal token? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139829-How-to-evaluate-the-value-of-PsiJavaToken-of-STRING-LITERAL-type)
How do I rename or move a Java class? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791825-How-to-rename-a-class-)
How do I build the list of all classes used by a given class or package? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139469-Using-DependencyValidationManager-to-Get-Required-Classes)
How do I insert whitespace into the PSI? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143839-Adding-PsiElements-to-a-PsiFile)
How do I add properties to a .properties file? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206142279-Dynamically-add-new-properties-to-properties-files)
How do I find specific method calls inside a PsiMethod? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143579-finding-a-statement-within-a-PsiMethod)
What is the lifecycle of a PSI element? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206796015-What-is-the-lifecycle-of-a-PsiElement-)
How do I create a new class in the given package? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206771665-Creating-a-new-class)
How do I make a PsiClass extend another PsiClass? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206794255-How-to-make-a-PsiClass-derive-from-another-one-)
How do I find references to a class from non-Java files? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800695-How-to-obtain-the-references-to-a-class-from-non-java-files-)
How do I find the source file given the path to a .class file? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800595-How-to-find-the-source-for-a-class-file)
How do I find classes with the specified non-qualified name? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206146759-How-to-resolve-unqualified-name-to-possible-PsiClasses-)
How do I change the value of an XML attribute through the PSI? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139639-Change-xml-attribute-value)
How do I programmatically register a DTD or schema? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206795425-How-to-register-DTD-with-idea)
How can I receive notifications about refactoring events? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206795955-Refactoring-Listeners)
How do I show a refactoring dialog programmatically? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800005-How-to-invoke-refactoring-dialog-not-refactoring-itself-)
How do I get access to class files generated by javac? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800625-Implementing-a-ClassInstrumentingCompiler-how-to-get-the-generated-class-files)
Can I provide line status markers for files in a custom file system? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791585-Editor-diff-functionality-for-custom-file-system)
How do I update the state of VCS actions depending on file status? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791975-VCS-context-menu)
How can I find out the module of a deleted file? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206792195-Module-for-deleted-file-)
Can I provide additional decorations for changelists in the Changes view? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139549-Is-it-possible-to-decorate-change-lists-)
How do I report out-of-date files? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791775-VCS-OpenAPI-what-to-do-with-files-detected-as-out-of-date-)
Why doesn't the file change on disk after I changed it through the PSI? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791625-Action-doesn-t-see-changes-in-xml-file)
Can I hook into the file save logic? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206790685-Can-you-tie-into-the-file-save-logic-)
Can I mark a part of a file as read-only? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/207042355-Read-only-section-in-editor)
How do I control what happens when the user tries to edit such a part? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791375-Using-locked-regions)
How can I show several editors for a single file in tabs? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206795495-Alternative-Editors-ala-HTML-Preview)
Can I open an editor which has no underlying file on disk? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206135449-Create-an-Editor-for-a-non-physical-file)
How do I highlight elements in a source code editor? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143909-MarkupModel-navigate-highlighted-elements)
How do I allow to navigate between highlighted elements using Find Next? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143879-HighlightManager-how-to-enable-F3-functionality)
How do I get the active editor instance? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206141119-how-to-get-the-Editor-from-PsiElement-)
How do I clear the read-only status of a file? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206142039-Clear-read-only-status)
How can I show an editor with error highlighting in a tool window? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206146679-Error-highlighting-in-Editors)
How can I provide a quick fix that creates a method? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206142769-Triggering-Create-Method-intention)
Is it possible to inspect only the elements that have been modified after the last full inspection? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800645-How-to-inspect-only-the-elements-modified-since-the-last-class-inspection)
ExternalAnnotator not in sync with current editor (https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000337510-Only-trigger-externalAnnotator-when-the-file-system-is-in-sync)
Can I add a new module dependency storage format? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206137859-Dependency-storage-formats-)
What is the Pair to be passed to JavaModuleBuilder.setSourcePaths()? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143559-Usage-of-class-Pair-A-B-)
How do I provide auto-popup code completion in my language? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139359-Autopopup-code-completion-in-custom-language)
How to make a closing brace unindent? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206797085-Custom-Language-How-to-make-a-closing-brace-unindent-)
How do I enable debugging for my custom language which is compiled into Java? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206786875-Debugging-custom-languages-)
How do I generate virtual Java classes mirroring the classes of my language? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143749-Custom-languages-masquarding-as-a-java-source-file-within-IntelliJ)
How do I provide animated status bar notifications? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206791945-IDE-Notifications)
How do I enable file name completion in a combo box? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139509-Combobox-with-Browse-Button-and-Autocompletion-)
How do I show a popup with left-aligned and right-aligned parts for each item? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139049-popup-menu-with-left-and-right-aligned-items)
How do I provide a custom icon for files/PSI elements? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206143779-Is-it-possible-to-change-icon-of-file-in-Project-view-)
Can I show a progress indicator for WriteActions? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206139159-WriteActions-and-ProgressIndicator)
How do I show a custom window or popup based on Structure View? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206142679-Opening-a-customised-StructureView)
How do I show the package selector dialog programmatically? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206794265-Package-selector-dialog)
How do I provide syntax and error highlighting in a combo box editor? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206800495-EditorTextField-in-3403-How-to-get-an-Editor-that-does-error-highlighting-)
How can I provide Close and Rerun buttons in my Usage View window? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206146779-How-to-get-a-Close-button-in-an-own-Usage-View-)
How can I display the SDK list in a JComboBox? (https://stackoverflow.com/questions/51499884/how-to-display-the-sdk-list-in-a-jcombobox)
How can I implement a custom stack trace analyzer? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206142959-Stack-Analyzer-extension)
How do I open a project programmatically? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206146969-how-to-open-a-project-)
How do I get the folder of the currently selected file? (https://intellij-support.jetbrains.com/hc/en-us/community/posts/206121889-How-to-get-the-folder-of-currenctly-selected-file)
This section describes the low-level fundamental building blocks of the IntelliJ Platform:
The component model - how the application is hosted and composed. Lifetime and dependency management.
Disposers (Disposer and Disposable) - managing object lifetimes and resource cleanup
Threading models (General Threading Rules)
Messaging (Messaging Infrastructure)
The IntelliJ Platform's Disposer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/Disposer.java) facilitates resource cleanup. If a subsystem keeps a set of resources alive coincident with a parent object's lifetime, the subsystem's resources should be registered with the Disposer to be released before or at the same time as the parent object.
The most common resource type managed by Disposer is listeners, but there are other possible types:
File handles, and database connections,
Caches and other significant data structures.
The Disposer is a singleton that manages a tree of Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java) instances. A Disposable is an interface for any object providing a Disposable.dispose() method to release heavyweight resources after a specific lifetime.
The Disposer supports chaining Disposable objects in parent-child relationships.
Many objects are disposed automatically by the platform if they implement the Disposable interface. The most important type of such objects is services (Services). Application-level services are automatically disposed by the platform when the IDE is closed or the plugin providing the service is unloaded. Project-level services are disposed on project close or plugin unload events.
Note that extensions registered in plugin.xml (Plugin Configuration File) are not automatically disposed. If an extension requires executing some code to dispose it, you need to define a service and to put the code in its dispose() method or use it as a parent disposable.
The primary purpose of the Disposer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/Disposer.java) singleton is to enforce the rule that a child Disposable never outlives its parent.
The Disposer organizes Disposable objects in a tree of parent-child relationships. The tree of Disposable objects ensures the Disposer releases children of a parent first. Parent objects always live longer than their children.
The following diagram shows a simplified example of Disposer's tree:
When My Project is closed and its disposal is triggered by the platform, the Disposer API will dispose My Listener and My Alarm before My Project, and Project Service A and Project Service B before Services of My Project.
See The Disposable Interface for more information about creating Disposable classes.
Registering a disposable is performed by calling Disposer.register():
Disposer.register(parentDisposable, childDisposable);To register a child Disposable, a parent Disposable of a suitable lifetime is used to establish the parent-child relationship. One of the parent Disposables provided by the IntelliJ Platform can be chosen, or it can be another Disposable.
Use the following guidelines to choose the correct parent:
For resources required for a plugin's entire lifetime, use an application or project level service (Services). Example: PythonPluginDisposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/openapi/src/com/jetbrains/python/PythonPluginDisposable.java).
For resources required while a dialog (Dialogs) is displayed, use DialogWrapper.getDisposable().
For resources required while a tool window (Tool Windows) tab is displayed, pass your instance implementing Disposable to Content.setDisposer().
For resources with a shorter lifetime, create a disposable using Disposer.newDisposable() and dispose it manually using Disposable.dispose(). Note that it's always best to specify a parent for such a disposable (e.g., a project-level service), so that there is no memory leak if the Disposable.dispose() call is not reached because of an exception or a programming error.
Plugin disposable leaksEven though Application and Project implement Disposable, they must NEVER be used as parent disposables in plugin code. Disposables registered using those objects as parents will not be disposed when the plugin is unloaded, leading to memory leaks.
Consider a case of a disposable resource created by a plugin and registered with a project as its parent. The following lifetime diagram shows that the resource will outlive the plugin and live as long as the project.
If the resource used, e.g., a plugin's project-level service (if shorter living parents are possible, prefer them), the resource would be disposed together with the plugin:
Inspection Plugin DevKit | Code | Incorrect parentDisposable parameter will highlight such problems.
The Disposer API flexibility means that if the parent instance is chosen unwisely, the child may consume resources for longer than required. Continuing to use resources when they are no longer needed can be a severe source of contention due to leaving some zombie objects behind due to each invocation. An additional challenge is that these kinds of issues won't be reported by the regular leak checker utilities, because technically, it's not a memory leak from the test suite perspective.
For example, suppose a UI component created for a specific operation uses a project-level service as a parent disposable. In that case, the entire component will remain in memory after the operation is complete. This creates memory pressure and can waste CPU cycles on processing events that are no longer relevant.
Many IntelliJ Platform APIs for registering listeners either require passing a parent disposable or have overloads that take a parent disposable. For example:
public abstract class EditorFactory {
// ...
public abstract void addEditorFactoryListener(@NotNull EditorFactoryListener listener);
public abstract void addEditorFactoryListener(@NotNull EditorFactoryListener listener, @NotNull Disposable parentDisposable);
public abstract void removeEditorFactoryListener(@NotNull EditorFactoryListener listener);
}Methods with a parentDisposable parameter automatically unsubscribe the listener when the corresponding parent disposable is disposed. Using such methods is always preferable to removing listeners explicitly from the dispose method because it requires less code and is easier to verify for correctness.
To choose the correct parent disposable, use the guidelines from the previous section.
The same rules apply to message bus (Messaging Infrastructure) connections. Always pass a parent disposable to MessageBus.connect(), and make sure it has the shortest possible lifetime.
You can use Disposer.isDisposed() to check whether a Disposable has already been disposed. This check is useful, for example, for an asynchronous callback to a Disposable that may be disposed before the callback is executed. In such a case, the best strategy is usually to do nothing and return early.
Non-disposed objects shouldn't hold onto references to disposed objects, as this constitutes a memory leak. Once a Disposable is released, it should be completely inactive, and there's no reason to refer to it anymore.
A plugin can manually end a Disposable lifecycle by calling Disposer.dispose(Disposable). This method handles recursively disposing of all the Disposable child descendants as well.
Creating a class requires implementing the Disposable interface and defining the dispose() method.
In many cases, when the object implements Disposable only to be used as a parent disposable, the method's implementation will be completely empty.
An example of a non-trivial dispose implementation is shown below:
public class Foo<T> extends JBFoo implements Disposable {
public Foo(@NotNull Project project,
@NotNull String name,
@Nullable FileEditor fileEditor,
@NotNull Disposable parentDisposable) {
this(project, name, fileEditor, InitParams.createParams(project),
DetachedToolWindowManager.getInstance(project));
Disposer.register(parentDisposable, this);
}
@Override
public void dispose() {
myFooManager.unregister(this);
myDetachedToolWindowManager.unregister(myFileEditor);
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.removePropertyChangeListener("focusOwner", myMyPropertyChangeListener);
setToolContext(null);
}
}A lot of code setting up all the conditions requiring release in dispose() has been omitted for simplicity.
Regardless, it illustrates the basic pattern, which is:
In this case, the parent disposable is passed into the constructor,
The Foo disposable is registered as a child of parentDisposable in the constructor.
The dispose() method consolidates the necessary release actions and will be called by the Disposer.
Never call Disposable.dispose() directly because it bypasses the parent-child relationships established in Disposer. Always call Disposer.dispose(Disposable) instead.
When the application exits, it performs a final sanity check to verify everything was disposed. If something was registered with the Disposer but remains undisposed, the IntelliJ Platform reports it before shutting down.
In test and Debug mode (idea.disposer.debug is set to on), registering a Disposable with the Disposer also registers a stack trace for the object's allocation path. The Disposer accomplishes this by creating a Throwable at the time of registration.
The following snippet represents the sort of "memory leak detected" error encountered in practice:
java.lang.RuntimeException:
Memory leak detected: <Instance> of class <com.example.classtype>
See the cause for the corresponding Disposer.register() stacktrace:
at ObjectTree.assertIsEmpty(ObjectTree.java:209)
at Disposer.assertIsEmpty(Disposer.java:125)
at Disposer.assertIsEmpty(Disposer.java:121)
at ApplicationImpl.disposeSelf(ApplicationImpl.java:323)
at ApplicationImpl.doExit(ApplicationImpl.java:780)
…
Caused by: java.lang.Throwable
at ObjectTree.getOrCreateNodeFor(ObjectTree.java:101)
at ObjectTree.register(ObjectTree.java:62)
at Disposer.register(Disposer.java:81)
at Disposer.register(Disposer.java:75)
…
at ProjectManagerEx.createProject(ProjectManagerEx.java:69)
at NewProjectWizardDynamic.doFinish(NewProjectWizardDynamic.java:235)
at DynamicWizard$1.run(DynamicWizard.java:433)
at CoreProgressManager$5.run(CoreProgressManager.java:237)
at CoreProgressManager$TaskRunnable.run(CoreProgressManager.java:563)
…The first part of the callstack is unrelated to diagnosing the memory leak. Instead, pay attention to the second part of the call stack, after Caused by: java.lang.Throwable.
In this specific case, the IntelliJ Platform (CoreProgressManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/openapi/progress/impl/CoreProgressManager.java)) started a task that contained the DynamicWizard code. In turn, that code allocated a Project that was never disposed by the time the application exited. That is a promising place to start digging.
The above memory leak was ultimately caused by failing to pass a Project instance to a function responsible for registering it for disposal. Often the fix for a memory leak is as simple as understanding the memory scope of the object being allocated - usually a UI container, project, or application - and making sure a Disposer.register() call is made appropriately for it.
In the IntelliJ Platform, code is executed on one of two thread types:
Event Dispatch Thread (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html) (EDT) - also known as the UI thread. It is used for updating the UI and performing changes in the IDE data model. Operations performed on EDT must be fast.
background threads - used for performing costly operations.
Thread Access Info (https://plugins.jetbrains.com/plugin/16815-thread-access-info) plugin visualizes Read/Write Access and Thread information in the debugger.
In general, code-related data structures in the IntelliJ Platform are covered by a single readers-writer (RW) lock (https://w.wiki/7dBy).
Access to the model must be performed in a read or write action for the following subsystems:
Virtual File System (VFS)
Project root model (Project Structure).
2023.3 Threading Model ChangesThreading model has changed in 2023.3, please make sure to choose the correct version in the tabs below.
Reading data is allowed from any thread.
Read operations need to be wrapped in a read action (RA) if not invoked via Application.invokeLater().
If invoked from a background thread or from EDT but via SwingUtilities.invokeLater(), it must be explicitly wrapped in a read action (RA).
Reading data is allowed from any thread.
Reading data from EDT does not require any special effort.
However, read operations performed from any other thread must be wrapped in a read action (RA).
The corresponding objects are not guaranteed to survive between several consecutive read actions. As a rule of thumb, whenever starting a read action, check if the PSI/VFS/project/module is still valid.
Application.runReadAction() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java)
ReadAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/ReadAction.java) run() or compute()
Writing data is only allowed from EDT, and write operations always need to be wrapped in a write action (WA).
Modifying the model is only allowed from write-safe contexts, including user actions and SwingUtilities.invokeLater() calls from them (see ).
You may not modify PSI, VFS, or project model from inside UI renderers or SwingUtilities.invokeLater() calls.
Application.runWriteAction() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java)
WriteAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/WriteAction.java) run() or compute()
To pass control from a background thread to EDT, instead of the standard SwingUtilities.invokeLater(), plugins should use ApplicationManager.getApplication().invokeLater(). The latter API allows specifying the modality state (ModalityState (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/ModalityState.java)) for the call, i.e., the stack of modal dialogs under which the call is allowed to execute:
The operation will be executed after all modal dialogs are closed. If any of the open (unrelated) projects displays a per-project modal dialog, the action will be performed after the dialog is closed.
The operation can be executed when the topmost shown dialog is the one that contains the specified component or is one of its parent dialogs.
ModalityState.defaultModalityState() will be used. This is the optimal choice in most cases that uses the current modality state when invoked from EDT. It has special handling for background processes started with ProgressManager: invokeLater() from such a process may run in the same dialog that the process started.
The operation will be executed as soon as possible regardless of modal dialogs. Please note that modifying PSI, VFS, or project model is prohibited from such runnables.
If EDT activity needs to access file-based index (Indexing and PSI Stubs) (e.g., it's doing any project-wide PSI analysis, resolves references, etc.), please use DumbService.smartInvokeLater(). That way, it is run after all possible indexing processes have been completed.
Background progresses are managed by ProgressManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) class, which has plenty of methods to execute the given code with a modal (dialog), non-modal (visible in the status bar), or invisible progress. In all cases, the code is executed on a background thread, which is associated with a ProgressIndicator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java) object. The current thread's indicator can be retrieved any time via ProgressIndicatorProvider.getGlobalProgressIndicator().
For visible progresses, threads can use ProgressIndicator to notify the user about current status: e.g., set text or visual fraction of the work done.
Progress indicators also provide means to handle cancellation of background processes, either by the user (pressing the Cancel button) or from code (e.g., when the current operation becomes obsolete due to some changes in the project). The progress can be marked as canceled by calling ProgressIndicator.cancel(). The process reacts to this by calling ProgressIndicator.checkCanceled() (or ProgressManager.checkCanceled() if no indicator instances at hand). This call throws a special unchecked ProcessCanceledException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/base/src/com/intellij/openapi/progress/ProcessCanceledException.java) (PCE) if the background process has been canceled.
All code working with PSI (Program Structure Interface (PSI)), or in other kinds of background processes, must be prepared for PCE being thrown at any point. This exception must never be logged but rethrown, and it'll be handled in the infrastructure that started the process. Use inspection Plugin DevKit | Code | 'ProcessCanceledException' handled incorrectly (2023.3).
The checkCanceled() should be called often enough to guarantee the process's smooth cancellation. PSI internals have a lot of checkCanceled() calls inside. If a process does lengthy non-PSI activity, insert explicit checkCanceled() calls so that it happens frequently, e.g., on each Nth loop iteration. Use inspection Plugin DevKit | Code | Cancellation check in loops (2023.1).
Throwing PCE from ProgressIndicator.checkCanceled() can be disabled for development (e.g., while debugging the code) by invoking:
Tools | Internal Actions | Skip Window Deactivation Events
Tools | Internal Actions | Disable ProcessCanceledException
These actions are available only if Internal Mode is enabled (Enabling Internal Mode).
Background threads shouldn't take plain read actions for a long time. The reason is that if EDT needs a write action (e.g., the user types something), it must be acquired as soon as possible. Otherwise, the UI will freeze until all background threads have released their read actions.
The best-known approach is to cancel background read actions whenever there's a write action about to occur, and restart that background read action later from scratch. Editor highlighting, code completion, Goto Class/File/... actions all work like this.
To achieve that, the lengthy background operation is started with a ProgressIndicator, and a dedicated listener cancels that indicator when write action is initiated. The next time the background thread calls checkCanceled(), a PCE is thrown, and the thread should stop its operation (and finish the read action) as soon as possible.
There are two recommended ways of doing this:
If on EDT, call ReadAction.nonBlocking() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/ReadAction.java) which returns NonBlockingReadAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/NonBlockingReadAction.java) (NBRA)
If already in a background thread, use ProgressManager.runInReadActionWithWriteActionPriority() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) in a loop, until it passes or the whole activity becomes obsolete.
In both approaches, always check at the start of each read action if the objects are still valid, and if the whole operation still makes sense (i.e., not canceled by the user, the project isn't closed, etc.). With ReadAction.nonBlocking(), use expireWith() or expireWhen() for that.
If the activity has to access file-based index (Indexing and PSI Stubs) (e.g., it's doing any project-wide PSI analysis, resolves references, etc.), use ReadAction.nonBlocking(...).inSmartMode().
In particular, don't traverse Virtual File System, parse PSI (Program Structure Interface (PSI)), resolve references (PSI References) or query indexes/stubs (Indexing and PSI Stubs).
There are still some cases when the platform itself invokes such expensive code (e.g., resolve in AnAction.update()), but these are being worked on. Meanwhile, please try to speed up what you can in your plugin as it will be generally beneficial and also improve background highlighting performance. For implementations of AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java), plugin authors should specifically review the documentation of AnAction.getActionUpdateThread() in the Actions section as it describes how threading works for actions.
WriteActions currently have to happen on EDT. To speed them up, as much as possible should be moved out of the write action into a preparation step which can be then invoked in the background (e.g., using ReadAction.nonBlocking(), see above).
Listeners must not perform any heavy operations. Ideally, they should only clear some caches.
It is also possible to schedule background processing of events, but be prepared that some new events might be delivered before the background processing starts — and thus the world might have changed by that moment or even in the middle of background processing. Consider using MergingUpdateQueue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/util/ui/update/MergingUpdateQueue.java) and ReadAction.nonBlocking() (see ) to mitigate these issues.
Massive batches of VFS events can be pre-processed in the background, see AsyncFileListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java) (2019.2 or later).
The IntelliJ Platform is a multithreading environment that executes many asynchronous and non-blocking tasks to avoid UI freezes. These tasks are usually executed in background threads, which is a standard approach in the JVM world.
Since version 1.1, Kotlin (Configuring Kotlin Support) has introduced coroutines as a lightweight and easy to implement alternative to threads. The IntelliJ Platform started adapting coroutines in its APIs and internal code, and since 2024.1 it is recommended to use the coroutines approach over threads.
Plugins must use the bundled Kotlin Coroutines library, see "Kotlin Coroutines Libraries (kotlinx.coroutines)" in "Configuring Kotlin Support".
The reason for coroutines being lightweight is the fact that they are not bound to OS native threads, as opposed to the JVM threads. It enables much less memory consumption and cheaper context switching, which makes the platform and plugins more performant. For example, it's straightforward to run 100.000 coroutines on a standard computer, which is not possible with threads as it would cause OutOfMemoryError.
Besides performance, there are more advantages of using coroutines:
Coroutines greatly simplify the way of writing non-blocking code. What was usually implemented with hard to understand, implement and maintain callbacks, with coroutines looks like regular sequential/imperative code.
Coroutines allow for implementing structured concurrency (coroutines can spawn child coroutines), which allows for easily managing the lifecycle of concurrent tasks and error handling. For example, cancelling a parent coroutine automatically cancels all child coroutines.
It is trivial to switch execution of the code parts between UI and background threads.
Coroutines provide very limited Java interoperability, and coroutine-based APIs can’t be used to the full extent from Java code.
Kotlin Coroutines are new to the IntelliJ Platform and are not yet widely adopted in public APIs. In the future, the number of coroutine-based APIs will grow and using only Java may not be enough to implement a fully functional plugin. It will be required to use Kotlin (Configuring Kotlin Support), at least partially, e.g., to implement coroutine-based Extension Points.
Before going to the next coroutine-related sections, it is highly recommended to go through the following resources. It will help understand coroutines and become fluent with available APIs:
Official Kotlin Coroutines Guide (https://kotlinlang.org/docs/coroutines-guide.html)
KotlinConf talks by Roman Elizarov (Kotlin Coroutines architect):
Introduction to Coroutines (https://www.youtube.com/watch?v=_hfBv0a09Jc)
Deep Dive into Coroutines on JVM (https://www.youtube.com/watch?v=YrrUCSi72E8)
Coroutines in Practice (https://www.youtube.com/watch?v=a3agLJQ6vt8)
Asynchronous Data Streams with Flow (https://www.youtube.com/watch?v=tYcqn48SMT8)
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
Kotlin's coroutines follow the principle of structured concurrency. It means that each coroutine is run in a specific CoroutineScope (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/), which delimits the lifetime of the coroutine. This ensures that they are not lost and do not leak. An outer scope does not complete until all its child coroutines are completed. Cancellation of the outer scope also cancels its child coroutines. Structured concurrency ensures that any errors in the code are properly reported and never lost.
IntelliJ Platform provides special coroutine scopes that help ensure proper structured concurrency of coroutines run from the platform or plugin code. After cancellation, the platform awaits the completion of each scope. Using correct parent scopes guarantees that child coroutines will be properly canceled when no longer needed, preventing resource leaks.
The following diagram presents the scopes and their parent-child relationships:
All scopes presented on the diagram are supervisor scopes (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html) — they ignore the failures of their children.
Each coroutine scope can have only one actual parent, pointed with solid arrow lines. Dashed arrow lines point to fictional parents, which follow the actual coroutine parent-child semantics:
a parent scope cancels children on its own cancellation
a parent scope awaits children before considering itself complete
a failed child cancels its parent (which effectively is not happening because presented scopes are supervisors)
The Application×Plugin and Project×Plugin are intersection scopes with two semantic parents (actual and fictional).
Root - the root scope spans all the coroutines. This is the standard root scope launched with runBlocking (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) coroutine builder.
Application - a scope associated with the Application (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) container (component manager) lifetime. It is canceled on application shutdown. This triggers cancellation of the Application×Plugin scope and, subsequently, its children, including the Project×Plugin scope.
Project - a scope associated with a Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) container (component manager) lifetime. It is canceled when a project is being closed. This triggers the cancellation of the Project×Plugin scope and, subsequently, its children.
Plugin - a scope associated with a plugin lifetime. It is canceled on unloading of the corresponding plugin. This triggers cancellation of the Application×Plugin scope and, subsequently, its children, including the Project×Plugin scope.
Application×Plugin - a scope which is an intersection of the Application and Plugin scopes. It is canceled when the application is shutdown or the corresponding plugin is unloaded. This triggers the cancellation of its children and the Project×Plugin scope and, subsequently, its children.
Project×Plugin - a scope which is an intersection of the Project and Plugin scopes. It is canceled when a project is being closed or the corresponding plugin is unloaded.
Intersection scopes enable creating coroutines whose lifetime is limited by application/project and plugin lifetimes, e.g., application/project services (Services) provided by a plugin.
The Application Service and Project Service scopes are bound to an application and project service ("Types" in "Services") lifetimes accordingly. They are children of the "Intersection Scopes" in "Coroutine Scopes", which means that they are canceled when the application/project is closed or a plugin is unloaded.
The service scope is provided to services via constructor injection. The following constructor signatures are supported:
MyService(CoroutineScope) for application and project services
MyProjectService(Project, CoroutineScope) for project services
Each service instance receives its own scope instance. The injected scopes' contexts contain Dispatchers.Default (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) and CoroutineName(serviceClass) (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-name/).
See "Launching Coroutine From Service Scope" in "Launching Coroutines" for full samples.
If a plugin requires running some code in a coroutine, the recommended approach is to create a separate service (Services) that will receive its own scope via constructor and launch the coroutine in this scope. This approach guarantees the usage of the correct scope, preventing leaks and canceling wrong scopes and killing all their (e.g., application's or project's) coroutines accidentally.
See the Launching Coroutines section for details.
The following sections describe the potential problems that would occur if the wrong coroutine scopes were used. This allows better understanding of the platform scopes and why the service approach mentioned above must be used.
Application and Project scopes are exposed with Application.getCoroutineScope() and Project.getCoroutineScope(). Never use these methods, as they are deprecated and will be removed in the future.
Using these scopes could easily lead to project or plugin class leaks.
Project leak:
application.coroutineScope.launch {
project.getService(PsiDirectoryFactory::class.java)
}Closing the project cancels its scope. The application scope remains active, and the project is leaked.
Plugin leak:
project.coroutineScope.launch {
project.getService(MyPluginService::class.java)
}Unloading of the plugin cancels its scope. The project scope remains active, and the plugin classes are leaked.
There is no API for retrieving Application×Plugin and Project×Plugin intersection scopes, but let's assume there is a method exposing the Project×Plugin scope:
/**
* Returns the correct intersection scope for the project and plugin
* by a given plugin class.
*/
fun Project.getCoroutineScope(pluginClass: Class<*>): CoroutineScopeUsing this scope could lead to a plugin leak:
project.getCoroutineScope(PluginBService::class.java).launch {
project.getService(PluginAService::class.java)
}Unloading of Plugin A cancels its scope. The scope of Plugin B remains active, and the Plugin A classes are leaked.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
Coroutines are always executed in a context (https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html) represented by CoroutineContext (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-coroutine-context/). One of the most important parts of the context is a dispatcher, which determines what thread or thread pool the corresponding coroutine is executed on.
In the IntelliJ Platform, coroutines are executed on three main dispatchers:
The Dispatchers.Default (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-default.html) dispatcher is used for performing CPU-bound tasks.
It ensures that the number of tasks running in parallel does not exceed the number of CPU cores. A hundred threads performing CPU-bound work on a machine with 10 CPU cores can result in threads competing for CPU time and excessive thread switching. This makes the IDE effectively slower, hence the limitation. Using the default dispatcher (or its limitedParallelism() (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/limited-parallelism.html) slice) enables a consistent CPU load.
The Dispatchers.IO (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html) dispatcher is used for performing IO operations like reading/writing to files, network, executing external processes, etc.
It must be used at the very deep moment in the trace right before the actual IO operation happens and exited as soon as the operation is finished. Example:
The Dispatchers.EDT (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/coroutines.kt) dispatcher is used for executing UI actions on the Swing Event Dispatch Thread. Dispatchers.EDT dispatches onto EDT within the context modality state ("Modality and invokeLater()" in "General Threading Rules").
In Kotlin, a standard dispatcher for UI-based activities is Dispatchers.Main (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html). In the IntelliJ Platform, the EDT dispatcher is also installed as Dispatchers.Main so both can be used, however always prefer Dispatchers.EDT. Use Dispatchers.Main only if the coroutine is IntelliJ Platform-context agnostic (e.g., when it can be executed outside the IntelliJ Platform context). Use Dispatchers.EDT when in doubt.
The dispatcher concept is a higher level of abstraction over threads. While the code is always executed on threads, do not think about dispatchers as specific thread instances.
A single coroutine is not bound to the same thread during the whole execution time. It may happen that a coroutine starts on thread A, is suspended, and finished on thread B, even if the whole is executed with the same dispatcher context.
Consider the following code snippet:
suspend fun doSomething() {
val fetchedData = suspendingTask()
withContext(Dispatchers.EDT) {
updateUI(fetchedData)
}
}
suspend fun suspendingTask(): Data {
// fetch data from the internet
}The following diagram presents one of the potential execution scenarios:
The code is executed as follows:
suspendingTask is started and partially executed on Thread 2.
suspendingTask is suspended when it waits for data fetched from the internet.
After receiving data, suspendingTask is resumed, but now it is executed on Thread 1.
Execution explicitly switches to the EDT dispatcher and updateUI is executed on EDT.
This behavior can result in unexpected consequences for code that relies on thread-specific data and assumes it will execute consistently on the same thread.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
There are two approaches to launching coroutines in the IntelliJ Platform:
Service with its own scope. (recommended)
The runBlockingCancellable function.
The recommended approach is creating a service (Services) that receives its scope ("Service Scopes" in "Coroutine Scopes") via the constructor injection and launching a coroutine from the service methods. Please note that while creating a service instance does allocate additional resources, using a dedicated service and scope remains a lightweight and, most importantly, safe solution for launching coroutines. It should be used whenever possible.
The pattern is as follows:
@Service
class MyApplicationService(
private val cs: CoroutineScope
) {
fun scheduleSomething() {
cs.launch {
// do something
}
}
}@Service(Service.Level.PROJECT)
class MyProjectService(
private val project: Project,
private val cs: CoroutineScope
) {
fun scheduleSomething() {
cs.launch {
// do something
}
}
}The injected scope is created per service, so each instance has its own isolated scope with a common parent, which is an intersection scope ("Intersection Scopes" in "Coroutine Scopes"). The injected scope is canceled when the container (application/project) is shut down or when the plugin is unloaded.
Using runBlockingCancellable is not recommended. Use service scopes whenever possible.
In a standard coroutine-based application, the bridge between the regular blocking code and the suspending code is the runBlocking (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) function.
In the IntelliJ Platform, a similar purpose is achieved by the runBlockingCancellable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) function. In addition to the same semantics as runBlocking, the action gets canceled when the current progress indicator or the current job is canceled.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
The IntelliJ Platform provides APIs that allow to track the progress of background processes and cancel their execution, when they are canceled by a user, or they become obsolete due to some changes in the data model.
Background processes can be executed in three contexts:
(obsolete)
Currently, the Progress Indicator context is the most widely used approach in the IntelliJ Platform. As the platform's execution model moves towards coroutines, this approach can be considered obsolete. Starting with 2024.1, it is recommended to execute new code in the suspending context.
Once the client code switches to a suspending or a blocking context, it should not switch back to a progress indicator context.
The following sections explain the contexts and provide information about process cancellation, progress tracking, and switching between different contexts.
Code executed in Kotlin coroutines is executed in a suspending context. Since 2024.1, this context is recommended for executing background tasks to maximize CPU utilization.
Note that executing code in a suspending context is possible only with Kotlin (Configuring Kotlin Support).
In a suspending context, methods such as ProgressManager.checkCanceled() or ModalityState.defaultModalityState() won't have any effect. Therefore, if their behavior is required, switch to a blocking context.
Inspection Plugin DevKit | Code | Forbidden in suspend context method usage reports calling blocking code from suspending context.
ensureActive() (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html) from Kotlin coroutine's API
Note that ProgressManager.checkCanceled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) does not work in a coroutine context.
ProgressStep (https://github.com/JetBrains/intellij-community/tree/master/platform/util/progress/src/impl/ProgressStep.kt) - a step-based progress reporting (see its KDoc for details)
RawProgressReporter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/progress/src/RawProgressReporter.kt) - a raw text, details, and fraction reporting (invoked via reportRawProgress() (https://github.com/JetBrains/intellij-community/tree/master/platform/util/progress/src/steps.kt))
Any report*Progress() (https://github.com/JetBrains/intellij-community/tree/master/platform/util/progress/src/steps.kt) function should be used inside with*Progress() or runWithModalProgressBlocking() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide/progress/src/tasks.kt). Otherwise, if there is no reporter in the context, using report*Progress() will have no effect.
to blocking context: blockingContext() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) - enables ProgressManager.checkCanceled(), forwards modality state, etc. This function has an opposite behavior to runBlockingCancellable().
to progress indicator: unavailable (coroutineToIndicator() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) is an internal API and exists only to aid platform migration)
Executing tasks in a blocking context means executing them on a thread without access to the coroutine context (basically, in non-suspending functions) and not under a progress indicator. Usually, plugins should not execute new code in the blocking context. Prefer the suspending context or fall back to progress indicator if a plugin cannot use Kotlin.
Functions which schedule execution via Application.executeOnPooledThread() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) and similar methods, and which rely on ProgressManager.checkCanceled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) should be annotated with @RequiresBlockingContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/concurrency/annotations/RequiresBlockingContext.java) to inform clients about the required switch to a blocking context.
Inspection Plugin DevKit | Code | Calling method should be annotated with @RequiresBlockingContext reports missing annotations.
Progress reporting is not available in the blocking context.
to coroutine: runBlockingCancellable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt). This function has an opposite behavior to blockingContext().
to progress indicator: unavailable (blockingContextToIndicator() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt) is internal API and exists only to aid platform migration)
Code executed via the Progress API (ProgressManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java), ProgressIndicator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java), etc.) is executed in a progress indicator context. See the running background processes ("Background Processes and ProcessCanceledException" in "General Threading Rules") section for details.
Obsolete approach since 2024.1Executing code under progress indicator is obsolete since 2024.1. It is advised to use Kotlin coroutines in new code.
Please note that obsolete status does not mean deprecation. Executing code using the Progress API is still allowed, but coroutines are recommended as a more performant solution.
ProgressManager.checkCanceled() - as described in the "Background Processes and ProcessCanceledException" in "General Threading Rules" section
current thread's ProgressIndicator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressIndicator.java) methods
ProgressManager's (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java) progress and progress2 static methods
to coroutine: runBlockingCancellable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt)
to blocking: unavailable
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
The concept of read/write locks and running blocking and cancellable read actions is explained in the Threading section:
This section explains running read actions (RA) in coroutines specifically.
Running RA from coroutines is executed with *ReadAction* functions from coroutines.kt (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/coroutines.kt) (see their KDocs for the details). Functions can be divided into two groups, which differ in reacting to an incoming write action (WA):
Write Allowing Read Action (WARA) | Write Blocking Read Action (WBRA) |
|---|---|
readAction | readActionBlocking |
smartReadAction | smartReadActionBlocking |
constrainedReadAction | constrainedReadActionBlocking |
WARA is canceled when a parent coroutine is canceled or a WA arrives.
WBRA is canceled only when a parent coroutine is canceled. It blocks WA until finishing its lambda.
Naming ConventionIt is important to note that in the coroutines context, default functions (without the Blocking suffix) behavior prioritizes WA.
In contrast, in the non-coroutine context, Application.runReadAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) and similar methods (without any prefix/suffix) perform RA blocking WA, whereas RA allowing WA are invoked via the NonBlockingReadAction API ("Read Action Cancellability" in "General Threading Rules").
Be careful when migrating the code running read actions to coroutines.
WARA API is simpler than NonBlockingReadAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/NonBlockingReadAction.java) (NBRA). WARA doesn't need the following API methods:
submit(Executor backgroundThreadExecutor) because this is a responsibility of the coroutine dispatcher
executeSynchronously() because effectively they are executed in the current coroutine dispatcher already
expireWhen(BooleanSupplier expireCondition)/expireWith(Disposable parentDisposable)/wrapProgress(ProgressIndicator progressIndicator) because they are canceled when the calling coroutine is canceled
finishOnUiThread() because this is handled by switching to the EDT dispatcher ("EDT Dispatcher" in "Coroutine Dispatchers"). Note that the UI data must be pure (e.g., strings/icons/element pointers), which inherently cannot be invalidated during the transfer from a background thread to EDT.
coalesceBy(Object ... equality) because this should be handled by Flow.collectLatest() (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/collect-latest.html) and/or Flow.distinctUntilChanged() (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/distinct-until-changed.html). Usually, NBRAs are run as a reaction to user actions, and there might be multiple NBRAs running, even if their results are unused. Instead of cancelling the read action, in the coroutine world the coroutines are canceled:
eventFlow.collectLatest { event ->
// the next emitted event will cancel the current coroutine
// and run it again with the next event
readAction { readData() }
}
eventFlow.distinctUntilChanged().collectLatest { event ->
// the next emitted event will cancel the current coroutine
// and run it again with the next event if the next event
// was not equal to the previous one
readAction { readData() }
}Suspending read actions use coroutines as the underlying framework.
WARA (invoked with mentioned *ReadAction functions) may make several attempts to execute its lambda. The block needs to know whether the current attempt was canceled. *ReadAction functions create a child Job (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/) for each attempt, and this job becomes canceled when a write action arrives. *ReadAction restarts the block if it was canceled by a write action, or throws CancellationException if the calling coroutine was canceled, causing the cancellation of the child Job.
To check whether the current action was canceled, clients must call ProgressManager.checkCanceled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java), which was adjusted to work in coroutines. Clients must not throw ProcessCanceledException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/base/src/com/intellij/openapi/progress/ProcessCanceledException.java) manually.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
This section presents techniques to use coroutines efficiently and avoid common pitfalls.
Avoid the invokeLater-style often found in Java:
launch(Dispatchers.EDT) {
val uiData = collectUiData()
// switch to Default:
launch(Dispatchers.Default) {
val result = compute(uiData)
// switch to EDT again:
launch(Dispatchers.EDT) {
applyUiData(result)
}
}
}The recommended approach:
launch(Dispatchers.EDT) {
val uiData = collectUiData()
// switch to Default:
val result = withContext(Dispatchers.Default) {
compute(uiData)
}
// this will be resumed on EDT automatically:
applyUiData(result)
}In some cases, it is required to exit the current EDT event and continue after all events in the queue are processed. In a non-coroutine context, it could be implemented like in the following snippet:
invokeLater {
step1()
invokeLater {
step2()
invokeLater {
step3()
}
}
}In a coroutine context, use the following approach:
withContext(Dispatchers.EDT) {
step1()
yield() // suspends here, dispatches the following block again on EDT
step2()
yield()
step3()
}This approach works with any sequential dispatcher, e.g., created with Dispatchers.Default.limitedParallelism(1).
The same applies to runBlockingCancellable ("Using runBlockingCancellable" in "Launching Coroutines"):
runBlockingCancellable {
println(1)
launch {
print(2)
yield()
print(3)
}
print(4)
yield()
print(5)
yield()
print(6)
}
// Output: 142536There is no scheduleWithFixedDelay() in coroutines, because it can be easily implemented with the following snippet:
val job = coroutineScope.launch {
delay(initialDelayMs)
while (true) {
action() // can be suspending as well
delay(delayMs)
}
}When the job is no longer needed, remember to cancel the launched coroutine:
job.cancel()or the whole scope:
coroutineScope.cancel()Each call of limitedParallelism() (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/limited-parallelism.html) creates a new independent dispatcher instance, effectively not limiting the parallelism:
suspend fun doSomething() {
withContext(Dispatchers.Default.limitedParallelism(3)) {
// ...
}
}Instead, store the dispatcher instance into a static property and use it as a context:
private val myDispatcher = Dispatchers.Default.limitedParallelism(3)
suspend fun doSomething() {
withContext(myDispatcher) {
// ...
}
}Do not use limitedParallelism(1) for code synchronization. Use Mutex (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/) instead.
Avoid changing modality state ("Modality and invokeLater()" in "General Threading Rules") in the middle of a running coroutine:
cs.launch {
// ...
withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) {
// ...
}
}Add the modality state to the context when launching a coroutine:
cs.launch(ModalityState.current().asContextElement()) {
// ...
withContext(Dispatchers.EDT) {
// ...
}
}It is possible that the coroutine is launched as a response to a user event from EDT, where ModalityState.current() is available.
If the coroutine is launched from a background thread, then it should not be invoked on top of an unrelated dialog anyway. The absence of the context modality state is effectively equivalent to specifying ModalityState.nonModal().
Kotlin Coroutines×IntelliJ PlatformThis section focuses on explaining coroutines in the specific context of the IntelliJ Platform (The IntelliJ Platform). If you are not experienced with Kotlin Coroutines, it is highly recommended to get familiar with "Learning Resources" in "Kotlin Coroutines" first.
The Help | Diagnostic Tools | Dump Threads action creates a thread dump, which is useful when investigating freezes or deadlocks. Thread dumps include all application threads and coroutines existing at the moment of dump creation.
A coroutine dump format is:
- parent coroutine header
at stackframe
at stackframe
...
- child coroutine 0 header
at stackframe
at stackframe
...
- grandchild coroutine header
at stackframe
at stackframe
...
- child coroutine 1 header
- child coroutine 2 header
at stackframe
at stackframe
...Each coroutine entry starts with a - character. Indentation represents parent-child relationships. A coroutine entry may not include a stacktrace (see child coroutine 1 header) because it has no executable body, or it did not start executing yet.
An example coroutine header:
-[x5 of] "my task":StandaloneCoroutine{Active}, state: SUSPENDED [ComponentManager(ApplicationImpl@xxxxxxxx), Dispatchers.EDT]Its format is as follows:
-[xN of] "name":CoroutineClass{JobState}, state: STATE [context]A particular subtree is repeated N times (included only if N > 1).
A coroutine name.
Notable names:
ApplicationImpl@xxxxxxxx container - application coroutine.
ProjectImpl@xxxxxxxx container - project coroutine.
com.intellij.*.AClass - a coroutine bound to some specific class instance, e.g., an extension or a service. Unnamed coroutines are hard to identify, so it is recommended to add CoroutineName(someName) into a coroutine context.
(a x b) - an intersection of coroutines a and b, e.g., (ApplicationImpl@56422718 x com.example.myplugin) is an intersection of the application and a plugin scope. See also "Intersection Scopes" in "Coroutine Scopes".
A coroutine's toString():
CoroutineClass - a coroutine class. Notable values:
StandaloneCoroutine and LazyStandaloneCoroutine are created by launch (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html).
DeferredCoroutine and LazyDeferredCoroutine are created by async (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html).
BlockingCoroutine is created by runBlockingCancellable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/progress/coroutines.kt).
ProducerCoroutine is created by produce (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html).
ChildScope is created by childScope (https://github.com/JetBrains/intellij-community/tree/master/platform/util/coroutines/src/coroutineScope.kt) or namedChildScope (https://github.com/JetBrains/intellij-community/tree/master/platform/util/coroutines/src/coroutineScope.kt).
JobState - a coroutine Job's state. Possible states and transition can be found in the Job's KDoc (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/).
A coroutine's state.
Possible states:
CREATED - a coroutine was created but not yet started.
SUSPENDED - a coroutine was executed up until the last frame in the stacktrace. This is where it was last seen running.
RUNNING - a coroutine is currently executed by a thread. Its stacktrace reflects what the coroutine is doing right now (probably blocked waiting for something, otherwise a RUNNING coroutine is rarely seen unless it’s doing CPU-intensive work).
A coroutine context. Context elements are separated with ,.
Notable context elements:
no parent and no name comes from the startup tracer. It should not be present in application/project coroutines and their children.
ComponentManager(ApplicationImpl@xxxxxxxx) - application or project, which serves as the coroutine parent.
BlockingEventLoop, Dispatchers.Default, Dispatchers.IO, LimitedDispatcher, Dispatchers.EDT - a coroutine dispatcher (Coroutine Dispatchers). Absence means Dispatchers.Default ("Default Dispatcher" in "Coroutine Dispatchers").
ModalityState.xxx - modality state. Absence means ModalityState.nonModal() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/ModalityState.java).
When EDT is in a modal state, all non-modal coroutines are suspended until the modal state ends, and EDT goes back to non-modal state as well.
Some RUNNING coroutines might block in an invokeAndWait call, which means that invokeAndWait used non-modal default modality state for one of two reasons:
the coroutine contains the correct modality state in its context, but invokeAndWait is not aware of it
a modal coroutine awaits another unrelated coroutine, which in turn requires non-modal EDT to complete.
Same problems can be found in regular thread dumps and blocking code, but coroutines suspend instead of blocking a thread, so it’s only possible to observe the last seen frame, which is usually enough.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
IntelliJ Platform's messaging infrastructure is an implementation of Publisher Subscriber Pattern (https://w.wiki/5xaV) that provides additional features like broadcasting on hierarchy and special nested events processing (a nested event is an event directly or indirectly fired from the callback of another event).
All available listeners/topics are listed on IntelliJ Platform Extension Point and Listener List under Listeners sections.
The following sections describe the main components of the messaging API:
Topic
Message Bus
Connection
The Topic (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/util/messages/Topic.java) class serves as an endpoint at the messaging infrastructure. Clients are allowed to subscribe to a specific topic within a bus and send messages to that topic within that particular bus. To clarify the corresponding message bus, a Topic field declaration should be annotated with Topic.@AppLevel and/or Topic.@ProjectLevel.
Human-readable name used for logging/monitoring purposes.
See for more details. The default value is TO_CHILDREN.
A business interface for a particular topic. Subscribers register an implementation of this interface at the messaging infrastructure. Publishers later retrieve objects that conform to the interface (IS-A) and call any methods defined on those implementations. The messaging infrastructure takes care of dispatching the message to all subscribers of the topic by calling the same method with the same arguments on the registered implementation callbacks.
MessageBus (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/util/messages/MessageBus.kt) is the core of the messaging system. It is used in the following scenarios:
Connection is represented by MessageBusConnection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/util/messages/MessageBusConnection.kt) class and manages all subscriptions for a particular client within a particular bus.
Connection stores topic-handler mappings - callbacks to invoke when message for the target topic is received (not more than one handler per topic within the same connection is allowed).
It's possible to specify default handler and subscribe to the target topic without explicitly provided callback. Connection will use that default handler when storing a topic-handler mapping.
It's possible to explicitly release acquired resources (see disconnect()). Also, it can be plugged to standard semi-automatic disposing (Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java)).
The sample below assumes a Project-level topic.
Create an interface with the business methods and a topic field bound to the business interface:
public interface ChangeActionNotifier {
@Topic.ProjectLevel
Topic<ChangeActionNotifier> CHANGE_ACTION_TOPIC =
Topic.create("custom name", ChangeActionNotifier.class);
void beforeAction(Context context);
void afterAction(Context context);
}If targeting 2019.3 or later, use declarative registration (Listeners) whenever possible.
project.getMessageBus().connect().subscribe(
ChangeActionNotifier.CHANGE_ACTION_TOPIC,
new ChangeActionNotifier() {
@Override
public void beforeAction(Context context) {
// Process 'before action' event.
}
@Override
public void afterAction(Context context) {
// Process 'after action' event.
}
});MessageBus instances are available via ComponentManager.getMessageBus() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/openapi/components/ComponentManager.java). Many standard interfaces implement returning a message bus, e.g., Application.getMessageBus() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) and Project.getMessageBus() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java).
public void doChange(Context context) {
ChangeActionNotifier publisher = project.getMessageBus()
.syncPublisher(ChangeActionNotifier.CHANGE_ACTION_TOPIC);
publisher.beforeAction(context);
try {
// do action
} finally {
publisher.afterAction(context);
}
}Message buses can be organised into hierarchies. Moreover, the IntelliJ Platform has them already:
That allows to notify subscribers registered in one message bus on messages sent to another message bus.
Example setup:
The example setup presents a simple hierarchy (the application bus is a parent of the project bus) with three subscribers for the same topic.
If topic1 defines broadcast direction as TO_CHILDREN, we get the following:
A message is sent to topic1 via application bus.
handler1 is notified about the message.
The message is delivered to the subscribers of the same topic within project bus (handler2 and handler3).
The main benefit of broadcasting is managing subscribers that are bound to child buses but interested in parent bus-level events. In the example above, we may want to have project-specific functionality that reacts to the application-level events. All we need to do is to subscribe to the target topic within the project bus. No hard reference to the project-level subscriber will be stored at application-level then, i.e., we just avoided memory leak on project re-opening.
Broadcast configuration is defined per-topic. The following options are available:
TO_CHILDREN (default)
TO_DIRECT_CHILDREN
NONE
TO_PARENT
See Topic.BroadcastDirection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/util/messages/Topic.java) for detailed description of each option.
Nested message is a message sent (directly or indirectly) during another message processing. The IntelliJ Platform's messaging infrastructure guarantees that all messages sent to particular topic will be delivered at the sending order.
Consider the following configuration:
When a message is sent to the target topic, the following happens:
message1 is sent
handler1 receives message1 and sends message2 to the same topic
handler2 receives message1
handler2 receives message2
handler1 receives message2
Messaging infrastructure is very light-weight, so it's possible to reuse it at local sub-systems in order to relieve Subscribers (https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) construction. Let's see what is necessary to do then:
Define business interface to work with.
Create shared message bus and topic that uses the interface above (shared here means that either subject or subscribers know about them).
A manual implementation would require:
Define listener interface (business interface).
Provide reference to the subject to all interested listeners.
Add listeners storage and listeners management methods (add/remove) to the subject.
Manually iterate all listeners and call target callback in all places where new event is fired.
We had a problem in a situation when two subscribers tried to modify the same document (IDEA-71701 (https://youtrack.jetbrains.com/issue/IDEA-71701)).
The thing is that every document change is performed by the following scenario:
before change event is sent to all document listeners and some of them publish new messages during that;
actual change is performed;
after change event is sent to all document listeners;
We had the following then:
message1 is sent to the topic with two subscribers;
message1 is queued for both subscribers;
message1 delivery starts;
subscriber1 receives message1;
subscriber1 issues document modification request at particular range (e.g. document.delete(startOffset, endOffset));
before change notification is sent to the document listeners;
message2 is sent by one of the standard document listeners to another topic within the same message bus during before change processing;
the bus tries to deliver all pending messages before queuing message2;
subscriber2 receives message1 and also modifies a document;
the call stack is unwound and actual change phase of document modification operation requested by subscriber1 begins;
The problem is that document range used by subscriber1 for initial modification request is invalid if subscriber2 has changed document's range before it.
If your plugin uses log4j library directly: it is removed from IntelliJ Platform in 2022.1; please see this blog post (https://blog.jetbrains.com/platform/2022/02/removing-log4j-from-the-intellij-platform/) for migration instructions.
The IntelliJ Platform uses Logger (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/diagnostic/Logger.java) abstraction class to shield from underlying logging implementation and configuration.
Plugins should obtain a dedicated instance:
import com.intellij.openapi.diagnostic.Logger;
public class MyPluginClass {
private static final Logger LOG = Logger.getInstance(MyPluginClass.class);
public void someMethod() {
LOG.info("someMethod() was called");
}
}import com.intellij.openapi.diagnostic.logger
private val LOG = logger<MyPluginClass>()
class MyPluginClass {
fun someMethod() {
LOG.info("someMethod() was called")
}
}If logging is used only to report exceptions, use convenience method thisLogger():
try {
// some code
} catch (e: Throwable) {
thisLogger().error("some code failed", e)
}By default, all messages with level INFO and higher are written to log output file idea.log. To enable DEBUG/TRACE logging for specific categories, use Help | Diagnostic Tools | Debug Log Settings.
To locate the log file, choose the Help | Show Log in Finder/Explorer action. When internal mode (Enabling Internal Mode) is enabled, the currently running IDE log file can be opened using Help | Open Log in Editor.
To locate it for a specific installation, see this Knowledge Base article (https://intellij-support.jetbrains.com/hc/en-us/articles/206544519). See Development Instance Sandbox Directory ("The Development Instance Sandbox Directory" in "IDE Development Instance") on how to find it for development instances.
See Testing FAQ (Testing FAQ) on how to enable DEBUG/TRACE level logging during tests, and obtain separate logs for failing tests.
To provide additional context for reporting fatal errors, use Logger.error() methods taking additional Attachment (see AttachmentFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/diagnostic/AttachmentFactory.java)).
The IDE will show fatal errors caught by itself as well as logging messages with ERROR level in the IDE Fatal Errors dialog:
for IDE platform: in EAP releases or when running in internal mode (Enabling Internal Mode)
for third-party plugins: always
For the latter, reporting is disabled by default — instead, there's an option to disable the plugin causing the exception.
To let users report such errors to the vendor, plugins can implement custom ErrorReportSubmitter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/diagnostic/ErrorReportSubmitter.java) registered in com.intellij.errorHandler extension point. See IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.errorHandler) for existing implementations — ranging from pre-filling web-based issue tracker forms to fully automated submission to log monitoring systems. This tutorial (https://www.plugin-dev.com/intellij/general/error-reporting/) also offers a working solution for using Sentry.
To disable red exclamation notification icon in status bar, invoke Help | Edit Custom Properties... and add idea.fatal.error.notification=disabled in opened idea.properties.
ApplicationInfo (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/ApplicationInfo.java) provides information on the IDE version and vendor. NOTE: to restrict compatibility, declare IDEs (Plugin Compatibility with IntelliJ Platform Products) and versions (Build Number Ranges) via plugin.xml (Plugin Configuration File).
To obtain information about OS and Java VM, use SystemInfo (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/SystemInfo.java).
To access relevant configuration directories, see PathManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/application/PathManager.java).
To obtain unique installation UUID, use PermanentInstallationID (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/application/PermanentInstallationID.java). For paid plugins, see also Marketplace docs (https://plugins.jetbrains.com/docs/marketplace/identify-user-of-paid-plugin.html).
To show custom context web-based help for your plugin's functionality (e.g., for dialogs (Dialogs)), provide WebHelpProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/help/WebHelpProvider.java) registered in com.intellij.webHelpProvider extension point.
Use RunOnceUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ide/util/RunOnceUtil.kt) to run a task exactly once per project/application.
Application lifecycle events can be tracked via AppLifecycleListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/AppLifecycleListener.java) listener (Listeners). See also "Application Startup" in "Components" and "Project and Application Close" in "Components".
Register ApplicationActivationListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/application/ApplicationActivationListener.java) listener (Listeners) to be notified of "application focused/unfocused" events.
To request restart of the IDE, use Application.restart() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java)
Use BrowserLauncher (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncher.kt).
Use RevealFileAction.openFile() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/actions/RevealFileAction.java) or openDirectory().
Use LafManagerListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ide/ui/LafManagerListener.java) topic to receive change notifications (e.g., to refresh UI).
File | Power Save Mode can be enabled to limit power-consuming features on laptops. Use PowerSaveMode (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/PowerSaveMode.java) service and PowerSaveMode.Listener topic to disable such features in your plugin accordingly.
Installed plugins can be checked via PluginManagerCore.isPluginInstalled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/ide/plugins/PluginManagerCore.kt).
For specific features (e.g., File Type (Registering a File Type), Facet (Facet), ...), the IDE will suggest installing matching plugins automatically. See Plugin Recommendations (https://plugins.jetbrains.com/docs/marketplace/intellij-plugin-recommendations.html) in Marketplace documentation for details.
To suggest other relevant plugins, use PluginsAdvertiser.installAndEnable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginsAdvertiser.kt).
To suggest replacing the currently installed deprecated plugin with the new one, implement PluginReplacement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ide/plugins/PluginReplacement.java) registered in com.intellij.pluginReplacement extension point.
Platform UI Guidelines: Overview (https://jetbrains.design/intellij/)
The IntelliJ Platform includes a large number of custom Swing (https://en.wikipedia.org/wiki/Swing_(Java)) components. Using those components in your plugins will ensure that your plugin looks and works consistently with the UI of the rest of the IDE, and can often reduce the code size compared to using the default Swing components.
Inspecting existing UIUse UI Inspector (Internal Actions - UI Inspector) to locate the underlying Swing component implementation or to inspect an existing UI at runtime.
UI forms like Dialogs or Settings should use Kotlin UI DSL (Kotlin UI DSL Version 2) (IntelliJ Platform 2021.3+).
Using UI Designer plugin with Kotlin is not supported (https://youtrack.jetbrains.com/issue/KTIJ-791).
Please refer to Writing short and clear (https://jetbrains.design/intellij/text/writing_short/) in IntelliJ Platform UI Guidelines on writing UI-related texts.
See UI Kit (https://jetbrains.design/intellij/resources/UI_kit/) when using Figma (https://www.figma.com) to design UI.
The following components are particularly noteworthy:
Menus and toolbars are built using Actions
Tables (TableView) (TBD)
Drag & Drop Helpers (TBD)
See also User Interface FAQ.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Product Help: Tool windows (https://www.jetbrains.com/help/idea/tool-windows.html)
Platform UI Guidelines: Tool window (https://jetbrains.design/intellij/components/tool_window/)
Tool windows are child windows of the IDE used to display information. These windows generally have their own toolbars (referred to as tool window bars) along the outer edges of the main window containing one or more tool window buttons, which activate panels displayed on the left, bottom, and right sides of the main IDE window.
Each side contains two tool window groups, the primary and the secondary one, and only one tool window from each group can be active at a time.
Each tool window can show multiple tabs (or "contents", as they are called in the API). For example, the Run tool window displays a tab for each active run configuration, and the Version Control related tool windows display a fixed set of tabs depending on the version control system used in the project.
There are two main scenarios for the use of tool windows in a plugin. Using declarative setup, a tool window button is always visible, and the user can activate it and interact with the plugin functionality at any time. Alternatively, using programmatic setup, the tool window is created to show the results of a specific operation, and can then be closed after the operation is completed.
The tool window is registered in plugin.xml (Plugin Configuration File) using the com.intellij.toolWindow extension point. The extension point attributes specify all the data which is necessary to display the tool window button:
The id attribute (required) of the tool window which corresponds to the text displayed on the tool window button. To provide a localized text, specify matching toolwindow.stripe.[id] message key (escape spaces with _) in the resource bundle ("resource-bundle" in "Plugin Configuration File") (code insight supported in 2020.3 and later).
The icon to display on the tool window button (13x13 pixels, grey and monochromatic; see Tool window (https://jetbrains.design/intellij/components/tool_window/#07) in IntelliJ Platform UI Guidelines and Working with Icons)
The anchor, meaning the side of the screen on which the tool window is displayed ("left" (default), "right" or "bottom")
The secondary attribute, specifying whether the tool window is displayed in the primary or the secondary group
The factoryClass attribute (required), a class implementing ToolWindowFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowFactory.kt).
When the user clicks on the tool window button, the createToolWindowContent() method of the factory class is called, and initializes the UI of the tool window. This procedure ensures that unused tool windows don't cause any overhead in startup time or memory usage: if a user does not interact with the tool window, no plugin code will be loaded or executed.
If the tool window of a plugin should not be displayed for all projects:
Implement ToolwindowFactory.isApplicableAsync(Project).
Implement ToolwindowFactory.isApplicable(Project).
Specify the conditionClass attribute in plugin.xml with a class implementing Condition<Project> (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/Condition.java) (can be the same class as the ToolWindowFactory implementation).
Note, the condition is evaluated only once when the project is loaded. To show and hide a tool window dynamically while the user is working with the project, use programmatic setup for tool window registration.
For toolwindows shown only after invoking specific actions, use ToolWindowManager.registerToolWindow(String,RegisterToolWindowTaskBuilder) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.kt).
Always use ToolWindowManager.invokeLater() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.kt) instead of "plain" Application.invokeLater() when scheduling EDT tasks related to tool windows (see General Threading Rules).
Displaying the contents of many tool windows requires access to indexes (Indexing and PSI Stubs). Because of that, tool windows are normally disabled while building indexes unless the ToolWindowFactory is marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
As mentioned previously, tool windows can contain multiple contents (tabs). To manage the contents of a tool window, call ToolWindow.getContentManager() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/wm/ToolWindow.java). To add a content (tab), first create it by calling ContentManager.getFactory().createContent() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ui/content/ContentManager.java), and then to add it to the tool window using ContentManager.addContent() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ui/content/ContentManager.java). Use Content.setDisposer() to register associated Disposable (see Disposer and Disposable).
See SimpleToolWindowPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/SimpleToolWindowPanel.java) as a convenient base class, supporting Toolbars ("Building UI from Actions" in "Actions") and both vertical/horizontal layout.
A plugin can control whether the user is allowed to close tabs either globally or on a per-content basis. The former is done by passing the canCloseContents parameter to the registerToolWindow() function, or by specifying canCloseContents="true" in plugin.xml. The default value is false; calling setClosable(true) on ContentManager content will be ignored unless canCloseContents is explicitly set.
If closing tabs is enabled in general, a plugin can disable closing of specific tabs by calling Content.setCloseable(false) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ui/content/Content.java).
Use ToolWindowManager.getToolWindow() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.kt) specifying the id used for registration.
ToolWindowManager.notifyByBalloon() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.kt) allows showing a notification for the given tool window.
Project-level topic ToolWindowManagerListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/ex/ToolWindowManagerListener.java) allows listening to tool window registration/show events (see Listeners).
To clarify how to develop plugins that create tool windows, consider the toolWindow sample plugin available in the code samples (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/tool_window).
See Code Samples on how to set up and run the plugin.
This plugin creates the Sample Calendar tool window that displays the system date, time and time zone. When opened, this tool window is similar to the following screen:

Platform UI Guidelines: Layout (https://jetbrains.design/intellij/principles/layout), Validation errors (https://jetbrains.design/intellij/principles/validation_errors/)
The DialogWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java) is the base class which is supposed to be used for all modal dialogs (and some non-modal dialogs) shown in IntelliJ Platform.
It provides the following features:
Button layout (platform-specific order of OK/Cancel buttons, macOS-specific Help button)
Context help
Remembering the size of the dialog
Non-modal validation (displaying an error message text when the data entered into the dialog is not valid)
Keyboard shortcuts:
Esc for closing the dialog
Left/Right for switching between buttons
Y/N for Yes/No actions if they exist in the dialog
Optional Do not ask again checkbox
There's also a DSL-like API via DialogBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogBuilder.java).
When using the DialogWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java) class for a dialog, follow these required steps:
Call the base class constructor and provide either a Project in the frame of which the dialog will be displayed, or a parent component for the dialog.
Call the setTitle() method to set the title for the dialog
Call the init() method from the constructor of the dialog class
Implement the createCenterPanel() method to return the component comprising the main contents of the dialog.
Optionally:
Override the getPreferredFocusedComponent() method and return the component that should be focused when the dialog is first displayed.
Override the getDimensionServiceKey() method to return the identifier which will be used for persisting the dialog dimensions.
Override the getHelpId() method to return the context help topic associated with the dialog (see Context Help ("Context Help" in "IDE Infrastructure")).
The DialogWrapper class is often used together with GUI Designer forms (https://www.jetbrains.com/help/idea/gui-designer-basics.html). In this case, bind a GUI Designer form to the class extending DialogWrapper, bind the top-level panel of the form to a field and return that field from the createCenterPanel() method. When using Kotlin, use Kotlin UI DSL (Kotlin UI DSL Version 2) to provide the dialog's contents.
See Layout (https://jetbrains.design/intellij/principles/layout) topic in IntelliJ Platform UI Guidelines for recommendations on arranging UI controls in dialogs.
Existing dialogs can be inspected at runtime using UI Inspector (Internal Actions - UI Inspector), e.g., to locate the underlying implementation of UI components.
To display the dialog, call the show() method and then use the getExitCode() method to check how the dialog was closed (see DialogWrapper#OK_EXIT_CODE|CANCEL_EXIT_CODE|CLOSE_EXIT_CODE). The showAndGet() method can be used to combine these two calls.
To customize the buttons displayed in the dialog (replacing the standard OK/Cancel/Help set of buttons), override either the createActions() or createLeftActions() methods. Both of these methods return an array of Swing Action objects. If a button closes the dialog, use DialogWrapperExitAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java) as the base class for the action. Use action.putValue(DialogWrapper.DEFAULT_ACTION, true) to set the default button.
Please see also Validation errors (https://jetbrains.design/intellij/principles/validation_errors/) topic in the IntelliJ Platform UI Guidelines.
To validate the data entered into the dialog, override the doValidate() method. The method will be called automatically by timer. If the currently entered data is valid, return null. Otherwise, return a ValidationInfo (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/ui/ValidationInfo.java) object which encapsulates an error message, and an optional component associated with the invalid data. When specifying a component, an error icon will be displayed next to it, and it will be focused when the user tries to invoke the OK action.
Simple definition of a DialogWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java):
public class SampleDialogWrapper extends DialogWrapper {
public SampleDialogWrapper() {
super(true); // use current window as parent
setTitle("Test DialogWrapper");
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
JPanel dialogPanel = new JPanel(new BorderLayout());
JLabel label = new JLabel("Testing");
label.setPreferredSize(new Dimension(100, 100));
dialogPanel.add(label, BorderLayout.CENTER);
return dialogPanel;
}
}Show SampleDialogWrapper dialog when user clicks on button:
JButton testButton = new JButton();
testButton.addActionListener(actionEvent -> {
if (new SampleDialogWrapper().showAndGet()) {
// user pressed OK
}
});Platform UI Guidelines: Layout (https://jetbrains.design/intellij/principles/layout)
This page describes API available in IntelliJ Platform releases 2021.3 and later only.
See Kotlin UI DSL Version 1 for targeting earlier releases.
Kotlin UI DSL Version 2 allows creating UI forms with input components bound to state objects. The forms are built by using a declarative Kotlin syntax and follow the official IntelliJ Platform UI conventions described in the IntelliJ Platform UI Guidelines (https://jetbrains.design/intellij/). The library is written in Kotlin (Configuring Kotlin Support) and makes it easy to develop user interfaces like dialogs and settings pages.
The Kotlin UI DSL is not intended to build general UIs, like tool windows controls that trigger some actions and do not contain any input components bound to state objects. For this purpose, use custom Swing components (User Interface Components) from the IntelliJ Platform or the standard ones.
The Kotlin UI DSL Version 2 functions are located in the com.intellij.ui.dsl.builder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder) package.
It is highly recommended taking a look at the UI DSL demo available via Tools | Internal Actions | UI | UI DSL Showcase (see Internal Actions (Internal Actions Menu) if not available in your IDE instance).
It describes some UI DSL basics and contains explanations, tips, a list of all available components, and many examples with links to the source code.
All sections below refer to relevant tab available in this demo:
UI DSL Showcase Tab: Tab Name (Link to sources of demonstration tab)
To understand how a component visible in the IDE is created in code, see the component's added-at property in the UI Inspector ("added-at Property" in "Internal Actions - UI Inspector"). Note that it is not limited only to components created with Kotlin UI DSL, but helps to understand creation of any visible Swing component.
UI DSL Showcase Tab: Basics (Sources: DemoBasics (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoBasics.kt))
See the following simple example of UI DSL:
panel {
row("Enter value:") {
textField()
}
}Building content of any form starts from panel { which returns DialogPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogPanel.kt) filled with components described inside the panel block. A panel consists of any number of rows marked with row tag created vertically from top to bottom.
Every row consists of cells where the last cell in a row occupies the remaining width. Inside one row, cells are added from left to right in the same order calls to factory methods or cell() appear in each row. Cells can contain one component or a sub-panel.
If there are unoccupied cells at the end of a row, they are merged into one cell with the last non-empty cell.
Panel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder/Panel.kt) is the start interface for building content. It can consist of several rows and different UI groups.
Adds row with the label if present.
UI DSL Showcase Tab: Gaps (Sources: DemoGaps (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGaps.kt))
Adds standard left indent:
row {
label("Not indented row")
}
indent {
row {
label("Indented row")
}
}UI DSL Showcase Tab: Groups (Sources: DemoGroups (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGroups.kt))
Adds horizontal line separator with an optional title.
Creates a sub-panel that occupies the whole width and uses its own grid inside.
Creates grouped rows range to perform operations on them like enabled, enabledIf etc. All rows use the parent grid.
UI DSL Showcase Tab: Groups (Sources: DemoGroups (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGroups.kt))
Adds a panel with an independent grid, optional title, and some vertical space above and below the group.
group("Title") {
row("Row:") {
textField()
}
}Similar to Panel.group() method but uses the same grid as the parent.
UI DSL Showcase Tab: Groups (Sources: DemoGroups (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGroups.kt))
Adds collapsible panel with independent grid, title, and some vertical space above and below the group.
collapsibleGroup("Title") {
row("Row:") {
textField()
}
}UI DSL Showcase Tab: Groups (Sources: DemoGroups (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGroups.kt))
Unions Row.radioButton in one group. Must also be used for Row.checkBox if these are grouped with some title.
var value = true
buttonsGroup("Panel.buttonsGroup:") {
row {
radioButton("true", true)
}
row {
radioButton("false", false)
}
}.bind({ value }, { value = it })Registers callbacks that will be called from DialogPanel.apply()/reset()/isModified() methods.
Every row is represented by the Row (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder/Row.kt) interface. It contains all available factory methods for creating components (like button(), label(), textField(), etc.) and methods for row configuration.
UI DSL Showcase Tab: Row Layout (Sources: DemoRowLayout (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoRowLayout.kt))
There are three possible layouts:
INDEPENDENT: all cells of the row (including label if present) independent of the parent grid. That means this row has its own grid.
LABEL_ALIGNED: label is aligned with the parent grid, other components independent of the parent grid.
PARENT_GRID: all components, including label (if present), are in the parent grid.
The default value is LABEL_ALIGNED when a label is provided for the row, INDEPENDENT otherwise.
row("Label:") {
textField()
}
row("Long label:") {
textField()
}Here both labels are aligned together because rows have LABEL_ALIGNED by default. If an independent layout is needed, then INDEPENDENT layout should be used:
row("Long label:") {
textField()
}.layout(RowLayout.INDEPENDENT)The row becomes resizable and occupies all vertical free space. For several resizable rows, extra free space is divided between rows equally.
UI DSL Showcase Tab: Comments (Sources: DemoComments (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoComments.kt))
Adds comment after the row with appropriate color and font. Visibility and enabled state of the row affects row comment as well.
Adds component. Use it only for custom specific components, all standard components like label(), button(), checkbox() etc. are covered by dedicated Row (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder/Row.kt) factory methods.
For example, there is no method for password field so that the following code can be used:
val passwordField = JPasswordField()
row {
cell(passwordField)
}Adds component wrapped with JBScrollPane (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java).
Adds additional gap above/below the current row. It is visible together with the row. By default, NONE is used. Between unrelated settings, SMALL can be used. MEDIUM is used between groups and usually set automatically by group() method and similar ones.
UI DSL Showcase Tab: Enabled/Visible (Sources: DemoAvailability (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoAvailability.kt))
Sets visibility/enabled state of the row, including row comment (see Row.rowComment) and all children recursively. The row is invisible/disabled if there is an invisible/disabled parent.
UI DSL Showcase Tab: Enabled/Visible (Sources: DemoAvailability (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoAvailability.kt))
Binds row visibility/enabled state to the provided predicate. Below is an example of a checkbox whose enabled state depends on another checkbox:
lateinit var checkBox: Cell<JBCheckBox>
row {
checkBox = checkBox("Check to enable option")
}
row {
checkBox("Option 1")
}.enabledIf(checkBox.selected)UI DSL Showcase Tab: Groups (Sources: DemoGroups (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGroups.kt))
Creates a sub-panel inside the cell of the row. The panel contains its own set of rows and cells. For example, it is possible to create several columns by creating a row with several panels inside.
Every component in the UI DSL builder is wrapped into Cell (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder/Cell.kt) class. Standard components should not be created directly but with factory methods from Row (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/dsl/builder/Row.kt) class like checkBox(), button() and others because of additional functionality, e.g. textField() is configured with columns width, radio buttons are placed into radio buttons groups.
JComponent that occupies the cell.
Deprecated in 2022.3, use instead.
Sets horizontal/vertical alignment of content inside the cell.
row("Row:") {
textField()
.horizontalAlign(HorizontalAlign.FILL)
}2022.3
Updates horizontal and/or vertical alignment of the component inside the cell. Default alignment is AlignX.LEFT and AlignY.CENTER.
To stretch the content on whole cell, use AlignX.FILL/AlignY.FILL/Align.FILL. For setting both horizontal and vertical alignment, use Align constants or overloaded plug operator like align(AlignX.LEFT + AlignY.TOP).
row("Row:") {
textField()
.align(AlignX.FILL)
}UI DSL Showcase Tab: Tips (Sources: DemoTips (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoTips.kt))
Marks column of the cell as resizable: the column occupies all extra horizontal space in panel and changes size together with the panel. It's possible to have several resizable columns, which means extra space is shared between them. There is no need to set resizable for cells in different rows but in the same column: it has no additional effect. Note that the size and placement of components in columns are managed by ( for pre-2022.3).
row("Row") {
textField()
.resizableColumn()
link("Config...") {}
}UI DSL Showcase Tab: Gaps (Sources: DemoGaps (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoGaps.kt))
Separates the next cell in the current row with rightGap. RightGap.SMALL gap is set after row label automatically by Panel.row() methods.
Below are some cases where RightGap.SMALL should be used:
row {
val checkBox = checkBox("Use mail:")
.gap(RightGap.SMALL)
textField()
}
row("Width:") {
textField()
.gap(RightGap.SMALL)
label("pixels")
}UI DSL Showcase Tab: Enabled/Visible (Sources: DemoAvailability (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoAvailability.kt))
Sets visibility/enabled state of the cell and all children recursively. The cell is invisible/disabled if there is an invisible/disabled parent.
UI DSL Showcase Tab: Enabled/Visible (Sources: DemoAvailability (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoAvailability.kt))
Binds cell visibility/enabled state to the provided predicate.
row {
val mailCheckBox = checkBox("Use mail:")
.gap(RightGap.SMALL)
textField()
.enabledIf(mailCheckBox.selected)
}UI DSL Showcase Tab: Comments (Sources: DemoComments (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoComments.kt))
Adds comment under the cell aligned by left edge with appropriate color and font size (macOS uses smaller font). Comment can contain HTML tags except <html>, which is added automatically. The comment occupies the available width before the following comment (if present) or the whole remaining width. Visibility and enabled state of the cell affect comment as well.
row("Label:") {
textField()
.comment("Comment for textField")
}UI DSL Showcase Tab: Components Labels (Sources: DemoComponentLabels (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoComponentLabels.kt))
Adds label at the specified position. LabelPosition.TOP labels occupy available width before the next top label (if present) or the whole remaining width. Visibility and enabled state of the cell affect the label as well.
row {
textField()
.label("Cell label on top:", LabelPosition.TOP)
}Registers callbacks that will be called for component from DialogPanel.apply()/reset()/isModified() methods.
Used as a reserved cell in the layout. It can be created by Row.placeholder() method and populated by content later via component property or reset to null.
It is possible to bind component values to properties with the following methods.
Removed in 2023.3, please use validationRequestor(property::afterPropagation) instead.
Binds component value changing to property. The property is updated when the value is changed and is not related to DialogPanel.apply(). The method is rarely used directly. There are many extensions for specific components described in .
UI DSL Showcase Tab: Binding (Sources: DemoBinding (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/internal/ui/uiDslShowcase/DemoBinding.kt))
Binds component value that is provided by componentGet and componentSet methods to specified binding property. The property is applied only when DialogPanel.apply() is invoked. Methods DialogPanel.isModified() and DialogPanel.reset() are also supported automatically for bound properties.
The bind() method is rarely used directly. There are many extensions for specific components, see following example:
row {
checkBox("Checkbox")
.bindSelected(model::checkbox)
}
row("textField:") {
textField()
.bindText(model::textField)
}
row("intTextField(0..100):") {
intTextField()
.bindIntText(model::intTextField)
}
row("comboBox:") {
comboBox(Color.values())
.bindItem(model::comboBoxColor)
}
row("slider:") {
slider(0, 100, 10, 50)
.bindValue(model::slider)
}
row("spinner:") {
spinner(0..100)
.bindIntValue(model::spinner)
}
buttonsGroup(title = "radioButton:") {
for (value in Color.values()) {
row {
radioButton(value.name, value)
}
}
}.bind(model::radioButtonColor)In UI DSL version 2, some crucial problems from version 1 have been fixed, so porting is highly desirable. See on how to port existing UI DSL code from version 1 to version 2 API. Version 1 is deprecated and will be removed in future platform releases.
The following significant changes were made:
Reduced API, which allows conceiving API easier and faster. Example: there were 5 overloaded methods Cell.checkBox() in version 1, now only one method remains. Functionality for binding properties is extracted into Cell<T>.bindSelected() methods.
UI DSL became stricter, so the available API in every context is much smaller. Example: code like row { row { is forbidden now.
Structured API mostly based on interfaces, because it's easier to learn API by grouped methods. Only a small part of API is implemented as extensions.
KDoc is widely used.
MIG layout is fully removed from the new UI DSL and replaced by GridLayout. Because MIG layout is an external library, it's hard to fix bugs there (e.g., there are layout problems when components become invisible) and extend its functionality. Fixed focus ring cropping problems: when components are placed near the panel border focus ring could be cropped if panel insets do not specify enough space.
Implemented Placeholder that allows replacing components at runtime after content is shown.
New API is very similar to the old one and covers almost all functionality now, so moving to the new version can be done quickly.
Version 1 is placed in com.intellij.ui.layout package.
Version 2 is placed in com.intellij.ui.dsl.builder package.
Having a screenshot or live version of the initial components layout can help
Remove imports of old UI DSL starting with com.intellij.ui.layout
Go to the place where the panel is created and import new UI DSL com.intellij.ui.dsl.builder suggested by IntelliJ IDEA
Update non-compilable code, using the following table
Version 1 | Version 2 |
|---|---|
row { row { | indent { row { |
row { cell(isFullWidth = true) | row { |
fullRow { | row { |
titledRow(…) { | group(…) { |
hideableRow | collapsibleGroup |
cell used as sub-grid | row { panel { … } } |
component(…) or its invocation via () | cell(…) |
enableIf | enabledIf |
checkBox(text, bindOptions) | checkBox(text).bindSelected(bindOptions) |
radioButton(text, bindOptions) | radioButton(text).bindSelected(bindOptions) |
comboBox(…, bindOptions) | comboBox(text).bindItem(bindOptions) |
textField(bindOptions, columns) | textField().bindText(bindOptions).columns(columns) |
scrollableTextArea(bindOptions, rows, columns) | textArea().bindText(bindOptions).rows(rows).columns(columns) |
intTextField(bindOptions, columns, range, step) | intTextField(range, step).bindIntText(bindOptions).columns(columns) |
textFieldWithBrowseButton(bindOptions, …) | textFieldWithBrowseButton(…).bindText(bindOptions) |
.growPolicy(GrowPolicy.COLUMNS_SHORT) | .columns(SHORT_TEXT) |
.growPolicy(GrowPolicy.MEDIUM_TEXT) | .columns(COLUMNS_MEDIUM) |
label(…, bold = true) | label(…).bold() |
withLeftGap() | For previous left cell use Cell.gap(SMALL) |
withLeftGap(gapLeft) | Please do not use custom gaps if possible |
withLargeLeftGap() | Not needed, this gap is set by default |
withValidationOnInput | validationOnInput |
withValidationOnApply | validationOnApply |
withErrorOnApplyIf | errorOnApply |
withBinding | bind |
Platform UI Guidelines: Layout (https://jetbrains.design/intellij/principles/layout)
If you're targeting IntelliJ Platform 2021.3 and later only, please use Kotlin UI DSL Version 2.
The version documented on this page is deprecated and will be removed in future platform releases.
Please note breaking changes (Incompatible Changes in IntelliJ Platform and Plugins API) can occur for this API between major releases.
Kotlin UI DSL allows creating UI forms with input components bound to state objects. The forms are built by using a declarative Kotlin syntax. It shares similarities with Jetpack Compose (https://developer.android.com/jetpack/compose) for Android and is intended to build UI forms or part of forms for, e.g. dialogs and settings pages.
The Kotlin UI DSL is not intended to build general UIs, like tool windows controls that trigger some actions and do not contain any input components bound to state objects. For this purpose, use custom Swing components (User Interface Components) from the IntelliJ Platform or the standard ones.
This document covers the Kotlin UI DSL in IntelliJ Platform 2019.2. A lot of the features described in this document are not available for plugins targeting earlier versions.
The Kotlin UI DSL Version 1 functions are located in the com.intellij.ui.layout (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/layout) package.
See Layout (https://jetbrains.design/intellij/principles/layout) topic in IntelliJ Platform UI Guidelines for recommendations on arranging UI controls in dialogs.
Use panel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/layout/layout.kt) to create UI:
panel {
row {
// child components
}
}Rows are created vertically from top to bottom, in the same order as lines of code that call row. Inside one row, you add components from left to right in the same order calls to factory method or () appear in each row. Every component is effectively placed in its own grid cell.
The label for the row can be specified as a parameter for the row method:
row("Parameters") { ... }Rows can be nested. Components in a nested row block are considered to be subordinate to the containing row and are indented accordingly.
row {
checkBox(...)
row {
textField(...) // indented relatively to the checkbox above
}
}To put multiple components in the same grid cell, wrap them in a cell method:
row {
// These two components will occupy two columns in the grid
label(...)
textField(...)
// These two components will be placed in the same grid cell
cell {
label(...)
textField(...)
}
}To put a component on the right side of a grid row, use the right method:
row {
rememberCheckBox()
right {
link("Forgot password")
}
}To visually debug layout, enable UI DSL Debug Mode from Internal Actions - UI Submenu (Internal Actions - UI Submenu).
There are two ways to add child components. The recommended way is to use factory methods label, button, radioButton, link, etc. It allows you to create consistent UI and reuse common patterns.
These methods also support property bindings, allowing you to automatically load the value displayed in the component from a property and to store it back. The easiest way to do that is to pass a reference to a Kotlin bound property:
checkBox("Show tabs in single row", uiSettings::scrollTabLayoutInEditor)Note that the bound property reference syntax also can be used to reference Java fields, but not getter/setter pairs.
Alternatively, many factory methods support specifying a getter/setter pair for cases when a property mapping is more complicated:
checkBox(
"Show file extensions in editor tabs",
{ !uiSettings.hideKnownExtensionInTabs },
{ uiSettings.hideKnownExtensionInTabs = !it })If you want to add a component for which there are no factory methods, you can simply invoke an instance of your component, using the () overloaded operator:
val customComponent = MyCustomComponent()
panel {
row { customComponent() }
}Use the label method:
label("Sample text")See examples above.
Radio button groups are created using the buttonGroup block. There are two ways to use it. If the selected radio button corresponds to a specific value of a single property, pass the property binding to the buttonGroup method and the specific values to radioButton functions:
buttonGroup(mySettings::providerType) {
row { radioButton("In native Keychain", ProviderType.KEYCHAIN) }
row { radioButton("In KeePass", ProviderType.KEEPASS) }
}If the selected radio button is controlled by multiple boolean properties, use buttonGroup with no binding and specify property bindings for all but one of the radio buttons:
buttonGroup {
row { radioButton("The tab on the left") }
row { radioButton("The tab on the right", uiSettings::activeRightEditorOnClose) }
row { radioButton("Most recently opened tab", uiSettings::activeMruEditorOnClose) }
}Use the textField method for a simple text field:
row("Username:") {
textField(settings::userName)
}For entering numbers, use intTextField:
intTextField(uiSettings::editorTabLimit, columns = 4, range = EDITOR_TABS_RANGE)For password text fields, there is no factory function available, so you need to use ():
val passwordField = JPasswordField()
val panel = panel {
// ...
row { passwordField() }
}To specify the size of a text field, either pass the columns parameter as shown in the intTextField example above, or use growPolicy():
val userField = JTextField(credentials?.userName)
val panel = panel {
row("Username:") { userField().growPolicy(GrowPolicy.SHORT_TEXT) }
}Use the comboBox method with either a bound property, or a getter/setter pair:
comboBox(DefaultComboBoxModel<Int>(tabPlacements), uiSettings::editorTabPlacement)
comboBox<PgpKey>(
pgpListModel,
{ getSelectedPgpKey() ?: pgpListModel.items.firstOrNull() },
{ mySettings.state.pgpKeyId = if (usePgpKey.isSelected) it?.keyId else null })Use the spinner method:
spinner(retypeOptions::retypeDelay, minValue = 0, maxValue = 5000, step = 50)Use the link method:
link("Forgot password?") {
// handle click, e.g. showing dialog
}To open URL in the browser, use browserLink:
browserLink("Open Homepage", "https://www.jetbrains.com")Use the titledRow method and put the controls under the separator into the nested block:
titledRow("Appearance") {
row { checkBox(...) }
}Use the comment parameter:
checkBox(message("checkbox.smart.tab.reuse"),
uiSettings::reuseNotModifiedTabs,
comment = message("checkbox.smart.tab.reuse.inline.help"))A panel returned by the panel method is an instance of DialogPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogPanel.kt). This base class supports the standard apply(), reset(), and isModified() methods.
Reference: DialogWrapper (Dialogs)
If you're using a DialogPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogPanel.kt) as the main panel of a DialogWrapper, the apply() method will be automatically called when the dialog is closed using OK action. The other methods are unused in this case.
Use the focused() method to specify which control should be focused when the dialog is initialized:
return panel {
row("Target class name:") {
textField(::className).focused()
}
}Reference: Settings Guide (Settings Guide)
If you're using the UI DSL to implement a Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java), use BoundConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/options/BoundConfigurable.kt) as the base class. In this case, the Configurable methods will be automatically delegated to the panel.
Use the enableIf method to bind the enabled state of a control to the values entered in other controls. The parameter of the method is a predicate.
checkBox("Show tabs in single row", uiSettings::scrollTabLayoutInEditor)
.enableIf(myEditorTabPlacement.selectedValueIs(SwingConstants.TOP))The available predicates are:
selected to check the selected state of a checkbox or radio button
selectedValueIs and selectedValueMatches to check the selected item in a combo box.
Predicates can be combined with and and or infix functions:
checkBox("Hide tabs if there is no space", uiSettings::hideTabsIfNeed)
.enableIf(myEditorTabPlacement.selectedValueMatches { it != UISettings.TABS_NONE } and
myScrollTabLayoutInEditorCheckBox.selected)Sample usages in IntelliJ Platform IDEs:
User Interface | Implementation |
|---|---|
Settings | Editor | Reader Mode | ReaderModeConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/actions/ReaderModeConfigurable.kt) |
New Branch dialog in Git (Manage Git branches (https://www.jetbrains.com/help/idea/manage-branches.html)) | GitNewBranchDialog (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/git4idea/src/git4idea/branch/GitNewBranchDialog.kt) |
Settings | Tools | Diff & Merge | DiffSettingsConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/diff-impl/src/com/intellij/diff/settings/DiffSettingsConfigurable.kt) |
Settings | Editor | General | Editor Tabs | EditorTabsConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.kt) |
To understand how a component visible in the IDE is created in code, see the component's added-at property in the UI Inspector ("added-at Property" in "Internal Actions - UI Inspector"). Note that it is not limited only to components created with Kotlin UI DSL, but helps to understand creation of any visible Swing component.
Set CCFlags.growX and CCFlags.pushX for some component in the second cell.
The IntelliJ Platform user interface makes extensive use of popups \- semi-modal windows that have no chrome (explicit closing buttons) and disappear automatically on focus loss. Making use of these controls in your plugin ensures a consistent user experience between your plugin and the rest of the IDE.
Popups can optionally display a title, are optionally movable and resizable (and support remembering their size), and can be nested (show another popup when an item is selected).
The JBPopupFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopupFactory.java) interface allows you to create popups that display different kinds of components, depending on your specific needs. The most commonly used methods are:
Method | Description |
|---|---|
createComponentPopupBuilder() | Generic, allows showing any Swing (https://docs.oracle.com/javase/tutorial/uiswing/start/index.html) component. Example: IntentionPreviewPopupUpdateProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt) creating a popup rendering the intention preview. |
createPopupChooserBuilder() | For choosing one or more items from a plain java.util.List. Example: ShowMessageHistoryAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/ShowMessageHistoryAction.kt) creating a popup with recent commit messages history in the commit message text area. |
createConfirmation() | For choosing between two options, and performing different actions depending on which option is selected. Example: VariableInplaceRenamer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/rename/inplace/VariableInplaceRenamer.java) creating confirmation popup after invalid variable name is provided in the inplace rename action. |
createActionGroupPopup() | Show actions from an Action Group (Grouping Actions) and executes the action selected by the user. Example: ShowRecentFindUsagesGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/find/impl/ShowRecentFindUsagesGroup.java) invoked via Edit / Find Usages / Recent Find Usages and showing recent find usages group popup. |
Action group popups support different ways of choosing an action from the keyboard, in additional to the normal arrow keys. By passing one of the constants in the JBPopupFactory.ActionSelectionAid (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopupFactory.java) enumeration, you can choose whether an action can be selected by pressing a key corresponding to its sequential number, typing part of its text (speed search) or pressing a mnemonic character. For popups with a fixed set of items, the recommended selection method is sequential numbering; for popups with a variable and potentially large number of items, speed search typically works best.
If you need to create a list-like popup which is more flexible than a simple JList (https://docs.oracle.com/javase/8/docs/api/javax/swing/JList.html) but don't want to represent the possible choices as actions in an action group, you can work directly with the ListPopupStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/ui/popup/ListPopupStep.java) interface and the JBPopupFactory.createListPopup() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/popup/JBPopupFactory.java) method. Normally you don't need to implement the entire interface; instead, you can derive from the BaseListPopupStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/popup/util/BaseListPopupStep.java) class. The key methods to override are getTextFor() (returning the text to display for an item) and onChosen() (called when an item is selected). By returning a new popup step from the onChosen() method, you can implement hierarchical (nested) popups.
Once you've created the popup, you need to display it by calling one of the show() methods. You can let the IntelliJ Platform automatically choose the position based on the context, by calling showInBestPositionFor(), or specify the position explicitly through methods like showUnderneathOf() and showInCenterOf().
The show() methods return immediately and do not wait for the popup to be closed.
If you need to perform some action when the popup is closed, you can either attach a listener to it using the addListener() method, override a method of the popup contents such as PopupStep.onChosen() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-ui/src/openapi/ui/popup/PopupStep.java), or attach an event handler to your own component within the popup.
Platform UI Guidelines: Notifications (https://jetbrains.design/intellij/controls/notifications/), Banner (https://jetbrains.design/intellij/controls/banner/), Got It tooltip (https://jetbrains.design/intellij/controls/got_it_tooltip/), Balloon (https://jetbrains.design/intellij/controls/balloon/)
One of the leading design principles is avoiding the use of modal message boxes for notifying the user about errors and other situations that may warrant the user's attention. As a replacement, the IntelliJ Platform provides multiple non-modal notification UI options.
When working in a dialog, instead of checking the validity of the input when the OK button is pressed and notifying the user about invalid data with a modal dialog, the recommended approach is to use DialogWrapper.doValidate() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java), described in the Dialogs ("Input Validation" in "Dialogs") section.
For actions invoked from the editor (such as refactorings, navigation actions, and different code insight features), the best way to notify the user about the inability to perform an action is to use the HintManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/codeInsight/hint/HintManager.java) class. Its method showErrorHint() displays a floating popup above the editor which is automatically hidden when the user starts performing another action in the editor. Other HintManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/codeInsight/hint/HintManager.java) methods can be used for displaying other kinds of non-modal notification hints over an editor.
For UI reference, see Banner (https://jetbrains.design/intellij/controls/banner/) in the IntelliJ Platform UI Guidelines.
Notifications that appear at the top of the file editor are a great way to ask the user to take an important action that would otherwise impede their experience if ignored (e.g., missing SDK, setup/project configuration requiring user input).
Register an implementation of EditorNotifications.Provider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/EditorNotifications.java) using com.intellij.editorNotificationProvider extension point. If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
A commonly used UI implementation is EditorNotificationPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/EditorNotificationPanel.java).
Use to highlight important new/changed features via GotItTooltip (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/GotItTooltip.kt). See Got It tooltip (https://jetbrains.design/intellij/controls/got_it_tooltip/) in IntelliJ Platform UI Guidelines for an overview.
The most general way to display non-modal notifications is to use the Notifications (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/notification/Notifications.java) class.
It has two main advantages:
The user can control the way each notification type is displayed under Settings | Appearance & Behavior | Notifications
All displayed notifications are gathered in the Event Log tool window and can be reviewed later
For UI reference, see Balloon (https://jetbrains.design/intellij/controls/balloon/) in the IntelliJ Platform UI Guidelines.
See "Tool Window Notification" in "Tool Windows" for showing balloons for a specific tool window.
The specific method used to display a notification is Notifications.Bus.notify() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/notification/Notifications.java). If the current Project is known, please use overload with Project parameter, so the notification is shown in its associated frame.
The text of the notification can include HTML tags for presentation purposes. Use Notification.addAction(AnAction) to add links below the content, use NotificationAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/notification/NotificationAction.java) for convenience.
The groupId parameter of the Notification (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/notification/Notification.java) constructor specifies a notification type. The user can choose the display type corresponding to each notification type under Settings | Appearance & Behavior | Notifications.
To specify the preferred display type, you need to use NotificationGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/notification/NotificationGroup.kt) to create notifications.
Please see the following steps for setup, depending on the target platform version.
NotificationGroup is registered in plugin.xml (Plugin Configuration File) using com.intellij.notificationGroup extension point. Use key to provide a localized group display name.
<extensions defaultExtensionNs="com.intellij">
<notificationGroup id="Custom Notification Group"
displayType="BALLOON"
key="notification.group.name"/>
</extensions>Registered instances can then be obtained via their id.
Code insight is available for parameters expecting a notification group id.
public class MyNotifier {
public static void notifyError(Project project, String content) {
NotificationGroupManager.getInstance()
.getNotificationGroup("Custom Notification Group")
.createNotification(content, NotificationType.ERROR)
.notify(project);
}
}NotificationGroup is registered in code.
public class MyNotifier {
private static final NotificationGroup NOTIFICATION_GROUP =
new NotificationGroup("Custom Notification Group",
NotificationDisplayType.BALLOON, true);
public static void notifyError(Project project, String content) {
NOTIFICATION_GROUP.createNotification(content, NotificationType.ERROR)
.notify(project);
}
}To let a user choose a file, directory or multiple files, use the FileChooser.chooseFiles() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java) method. This method has multiple overloads. The best method to use is the one which returns void and takes a callback receiving the list of selected files as a parameter. This is the only overload which will display a native file open dialog on macOS.
The FileChooserDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java) class allows you to control which files can be selected. The constructor parameters specify whether files and/or directories can be selected, and whether multiple selection is allowed (see FileChooserDescriptorFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/fileChooser/FileChooserDescriptorFactory.java) for common variants).
For more fine-grained control over the allowed selection, you can overload the isFileSelectable() method. You can also customize the presentation of files by overriding getIcon(), getName() and getComment() methods. Note that the native macOS file chooser does not support most of the customizations, so if you rely on them, you need to use an overload of chooseFiles() which displays the standard IntelliJ Platform dialog.
A very common way of using file choosers is to use a text field for entering the path with an ellipsis button (...) for showing the file chooser. To create such a control, use the TextFieldWithBrowseButton (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/TextFieldWithBrowseButton.java) component and call the addBrowseFolderListener() method on it to set up the file chooser. As an added bonus, this will enable filename completion when entering paths in the text box.
An alternative UI for selecting files, which works best when the most common way of selecting a file is by typing its name, is available through the TreeFileChooserFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/util/TreeFileChooserFactory.java) class.
The dialog shown by this API has two tabs:
One shows the project structure
Another shows a list of files similar to the one used by the Navigate | File popup.
To show the dialog, call showDialog() on the chooser returned from createFileChooser(), and then call getSelectedFile() to retrieve the user's selection.
If you want to offer the user a possibility to select a Java class, you can use the TreeClassChooserFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/openapi/src/com/intellij/ide/util/TreeClassChooserFactory.java) class. Its different methods allow you to specify the scope from which the classes are taken, to restrict the choice to descendants of a specific class or implementations of an interface, and to include or exclude inner classes from the list.
For choosing a Java package, you can use the PackageChooserDialog (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/ide/util/PackageChooserDialog.java) class.
To use Java-specific components, an explicit dependency on the Java plugin is required. See "Java" in "Plugin Compatibility with IntelliJ Platform Products" for details.
Compared to Swing JTextArea (https://docs.oracle.com/javase/8/docs/api/javax/swing/JTextArea.html), the IntelliJ Platform's editor component has a ton of advantages: syntax highlighting support, code completion, code folding, and much more. Editors (Editors) are normally displayed in editor tabs, but they can be embedded in dialogs or tool windows, too. This is enabled by the EditorTextField (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/EditorTextField.java) component.
The following attributes can be specified:
The file type according to which the text in the text field is parsed;
Whether the text field is read-only or editable;
Whether the text field is single-line or multiline.
Further customizations are possible by subclassing and overriding createEditor() and applying EditorCustomization (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/EditorCustomization.java). Several commonly needed customization implementations exist, including:
SpellCheckingEditorCustomization (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/ui/SpellCheckingEditorCustomization.java) disables spellchecking
HorizontalScrollBarEditorCustomization (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/HorizontalScrollBarEditorCustomization.java) to turn on/off horizontal scrollbar
ErrorStripeEditorCustomization (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/ErrorStripeEditorCustomization.java) to turn on/off error stripes on the right
EditorTextField has a number of subclasses that can be used as needed for additional features.
If you want to use an editor as an input field in a dialog, then consider using LanguageTextField (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/LanguageTextField.java) as it provides a more accessible API.
If you want to add autocompletion to the editor, then use TextFieldWithCompletion (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/util/textCompletion/TextFieldWithCompletion.java). The constructor takes as an argument a class that implements TextCompletionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/util/textCompletion/TextCompletionProvider.java) to provide autocompletion variants. Use TextFieldCompletionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/util/TextFieldCompletionProvider.java) to create your own provider. For this, override addCompletionVariants() and add completion variants using CompletionResultSet.addElement().
See also TextFieldCompletionProviderDumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/util/TextFieldCompletionProviderDumbAware.java) for completion even at the indexing stage.
See Code Completion to learn more about completion.
If your plugin depends on Java functionality and targets 2019.2 or later, see "Java" in "Plugin Compatibility with IntelliJ Platform Products".
A common use case for EditorTextField is entering the name of a Java class or package. This can be accomplished with the following steps:
Use JavaCodeFragmentFactory.createReferenceCodeFragment() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/JavaCodeFragmentFactory.java) to create a code fragment representing the class or package name;
Call PsiDocumentManager.getDocument() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiDocumentManager.java) to get the document corresponding to the code fragment;
Pass the returned document to the EditorTextField (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/EditorTextField.java) constructor or its setDocument() method.
PsiFile psiFile = PsiDocumentManager.getInstance(project)
.getPsiFile(editor.getDocument());
PsiElement element =
psiFile.findElementAt(editor.getCaretModel().getOffset());
PsiExpressionCodeFragment code =
JavaCodeFragmentFactory.getInstance(project)
.createExpressionCodeFragment("", element, null, true);
Document document =
PsiDocumentManager.getInstance(project).getDocument(code);
EditorTextField editorTextField =
new EditorTextField(document, project, JavaFileType.INSTANCE);When creating more than one field, two separate documents are needed. This is accomplished by using separate instances of PsiExpressionCodeFragment.
setText() no longer works for the input field. However, createExpressionCodeFragment() accepts the text for the field as an argument. The empty string can be replaced and create a new document in lieu of setText().
Instances of JTextField in the GUI builder can be replaced with a custom replacement component using the right click in your IDE. Make sure to use "Custom Create" so the initialization code works properly.
Whenever you would normally use a standard Swing JList (https://docs.oracle.com/javase/8/docs/api/javax/swing/JList.html) component, it's recommended to use the JBList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBList.java) class as drop-in replacement. JBList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBList.java) supports the following additional features on top of JList (https://docs.oracle.com/javase/8/docs/api/javax/swing/JList.html):
Drawing a tooltip with complete text of an item if the item doesn't fit into the list box width.
Drawing a gray text message in the middle of the list box when it contains no items. The text can be customized by calling getEmptyText().setText().
Drawing a busy icon in the top right corner of the list box to indicate that a background operation is being performed. This can be enabled by calling setPaintBusy().
Similarly, the Tree (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java) class provides a replacement for the standard JTree (https://docs.oracle.com/javase/8/docs/api/javax/swing/JTree.html) class. In addition to the features of JBList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBList.java), it supports wide selection painting (Mac style) and auto-scroll on drag & drop.
When you need to customize the presentation of items in a list box or a tree, it's recommended to use the ColoredListCellRenderer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/ColoredListCellRenderer.java) or ColoredTreeCellRenderer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java) classes as the cell renderer. These classes allow you to compose the presentation out of multiple text fragments with different attributes by calling append() and to set an optional icon for the item by calling setIcon(). The renderer automatically takes care of setting the correct text color for selected items and of many other platform-specific rendering details.
To facilitate keyboard-based selection of items in a list box or a tree, you can install a speed search handler on it using the ListSpeedSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java) and TreeSpeedSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/TreeSpeedSearch.java). This can be done simply by calling new ListSpeedSearch(list) or new TreeSpeedSearch(tree). To customize the text which is used to locate the element, override the getElementText() method. Alternatively, you can pass a function to convert items to strings. A function needs to be passed as elementTextDelegate to the ListSpeedSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/ListSpeedSearch.java) constructor or as toString to the TreeSpeedSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/TreeSpeedSearch.java) constructor.
A very common task in plugin development is showing a list or a tree where the user is allowed to add, remove, edit or reorder the items. The implementation of this task is greatly facilitated by the ToolbarDecorator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java) class. This class provides a toolbar with actions on items and automatically enables drag & drop reordering of items in list boxes if supported by the underlying list model. The position of the toolbar above or below the list depends on the platform under which the IDE is running.
To use a toolbar decorator:
If you need to support removing and reordering of items in a list box, make sure the model of your list implements the EditableModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/EditableModel.java) interface. CollectionListModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/CollectionListModel.java) is a handy model class that implements this interface.
Call ToolbarDecorator.createDecorator() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/ToolbarDecorator.java) to create a decorator instance.
If you need to support adding and/or removing items, call setAddAction() and/or setRemoveAction().
If you need other buttons in additional to the standard ones, call addExtraAction() or setActionGroup().
Call createPanel() and add the component it returns to your panel.
Product Help: Status bar (https://www.jetbrains.com/help/idea/guided-tour-around-the-user-interface.html#status-bar)
The IntelliJ Platform allows plugins to extend the IDE status bar with additional custom widgets.
Status bar widgets are small UI elements that allow providing users with useful information and settings for the current file, project, IDE, etc. For example, the status bar contains the widget showing the encoding of the current file, or the current VCS branch of the project.
Due to the prominent presentation and limited space, they should be used only for information or settings that are relevant enough to be "always" shown.
The starting point for extending the status bar with new widgets is the StatusBarWidgetFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/StatusBarWidgetFactory.java) interface, which is registered in the com.intellij.statusBarWidgetFactory extension point. Note: id attribute must be provided in plugin.xml registration and match value from StatusBarWidgetFactory.getId().
In case a widget provides information or functionality related to the editor files, consider extending the StatusBarEditorBasedWidgetFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/widget/StatusBarEditorBasedWidgetFactory.kt) class.
Each widget factory returns a new widget from createWidget(). To control the disposing of a widget, implement the disposeWidget(), if you just want to dispose it, use Disposer.dispose(widget).
Any widget must implement the StatusBarWidget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/wm/StatusBarWidget.kt) interface.
To reuse the IntelliJ Platform implementation, you can extend one of two classes:
EditorBasedWidget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedWidget.kt)
EditorBasedStatusBarPopup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/EditorBasedStatusBarPopup.kt)
EditorBasedWidget is the basic widget implementation. To implement it, override ID() where returns the unique ID of the widget. This identifier may be needed to later obtain a widget instance.
Use one of the existing predefined widget appearance options:
com.intellij.openapi.wm.StatusBarWidget.IconPresentation
Widget with only an icon.
Example: PowerSaveStatusWidgetFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PowerSaveStatusWidgetFactory.java)
com.intellij.openapi.wm.StatusBarWidget.TextPresentation
Widget with only a text.
Example: PositionPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/PositionPanel.kt)
com.intellij.openapi.wm.StatusBarWidget.MultipleTextValuesPresentation
Widget with a text and a popup.
Example: DvcsStatusWidget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/dvcs-impl/src/com/intellij/dvcs/ui/DvcsStatusWidget.java)
Note that they can't be combined to get, for example, an icon and a text.
To use the selected appearance, return a class that implements one of the above interfaces from getPresentation().
To create a widget with custom content, it should implement the CustomStatusBarWidget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/CustomStatusBarWidget.java) interface. Override getComponent() to return the custom widget's component to display.
Example: MemoryUsagePanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/MemoryUsagePanel.java)
EditorBasedStatusBarPopup is the basis for all widgets that have a popup with a list of actions. For example, the encoding widget of the current file.
The component to display is returned from createComponent(). Each update of the widget IDE calls updateComponent() to update this component. In updateComponent() implementation, you can describe how the widget should change depending on the current state.
Implement getWidgetState() to return the current state of the widget. This state will be passed to the updateComponent() when the widget is updated. The method accepts a file that's currently opened in the editor To create your own state class, inherit it from EditorBasedStatusBarPopup.WidgetState.WidgetState.
Implement ID(), and return the unique ID of the widget. This identifier may be needed to later get a widget instance.
Implement createInstance(), and return the new widget instance.
Finally, implement the createPopup() method, which returns the popup (Popups) that will be displayed when the widget is clicked.
Custom listeners to be notified of widget updates can be registered using registerCustomListeners().
To update a widget, use update().
By default, widgets aren't shown in LightEdit (https://www.jetbrains.com/help/idea/lightedit-mode.html) mode. To show a widget, implement LightEditCompatible (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/lightEdit/LightEditCompatible.java) in your factory.
The Messages (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/Messages.java) class provides a way to show simple message boxes, input dialogs (modal dialogs with a text field), and chooser dialogs (modal dialogs with a combo box). The function of different methods of the class should be clear from their names. When running on macOS, the message boxes shown by the Messages class use the native UI.
The showCheckboxMessageDialog() function provides an easy way to implement a Do not show this again checkbox on messages.
Note that it is recommended to use non-modal notifications instead of modal message boxes whenever it's appropriate. Please refer to the Notifications (Notifications) topic for more information.
The JBSplitter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/JBSplitter.java) class is JetBrains' replacement for the standard JSplitPane (https://docs.oracle.com/javase/8/docs/api/javax/swing/JSplitPane.html) class. Unlike some other JetBrains-enhanced Swing components, it's not a drop-in replacement and has a different API. However, to achieve a consistent user experience, it's recommended to use JBSplitter instead of the standard JSplitPane.
To add components to the splitter, call the setFirstComponent() and setSecondComponent() methods.
JBSplitter supports automatic remembering of the split proportion. To enable it, call the setSplitterProportionKey() method and pass the ID under which the proportion will be stored.
The JBTabs (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/tabs/JBTabs.java) class is JetBrains' implementation of the tab control, used for editor tabs and a few other components. It has a significantly different look & feel compared to the standard Swing tabs, and looks less native on the macOS platform, so it's up to the developer to choose which tab control would be more appropriate.
See Toolbar (https://jetbrains.design/intellij/controls/toolbar/) in the IntelliJ Platform UI Guidelines for an overview.
Building UI from Actions ("Building UI from Actions" in "Actions") covers creating AnAction-based toolbars.
Code: AllIcons (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/icons/AllIcons.java)
Platform UI Guidelines: Icons list (https://jetbrains.design/intellij/resources/icons_list/), Icons (https://jetbrains.design/intellij/principles/icons/)
Icons are used widely by IntelliJ Platform plugins. Plugins need icons mostly for Actions, custom component renderers, Tool Windows, etc.
Plugin Logos, which represent a plugin itself, have different requirements than icons used within a plugin. For more information, see the Plugin Logo.
Plugins should reuse existing platform icons whenever possible. Use Icons list (https://jetbrains.design/intellij/resources/icons_list/) to browse existing icons. Platform icons are located in AllIcons (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/icons/AllIcons.java). Icons from plugins are located in corresponding <PLUGIN_NAME>Icons class (e.g., GithubIcons (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/github/gen/org/jetbrains/plugins/github/GithubIcons.java)).
If custom icons are required, please refer to detailed design guide (https://jetbrains.design/intellij/principles/icons/). To generate SVG icons suited for the IntelliJ-based IDEs, also consider third-party web tool IntelliJ Icon Generator (https://bjansen.github.io/intellij-icon-generator/).
See Action Basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics) sample plugin as a reference.
In the case of a Gradle-based project, icons should be placed in the resources folder. If the project is DevKit-based, the recommended approach is to put icons to a dedicated source root (https://www.jetbrains.com/help/idea/content-roots.html) marked as Resources Root, e.g., icons or resources.
If the icons are referenced only in plugin.xml (Plugin Configuration File) attributes or elements, or in the @Presentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/ide/presentation/Presentation.java) icon attribute, then they can be referenced by paths. In case the icons are referenced from the code and/or XML many times, it's convenient to organize them in an icons holder class.
Define a class/interface in a top-level package called icons holding icon constants as static fields:
package icons;
public interface MyIcons {
Icon MyAction = IconLoader.getIcon("/icons/myAction.png", MyIcons.class);
Icon MyToolWindow = IconLoader.getIcon("/icons/myToolWindow.png", MyIcons.class);
}When using Kotlin, fields must be annotated with @JvmField:
package icons
object MyIcons {
@JvmField
val MyAction = IconLoader.getIcon("/icons/myAction.png", javaClass)
@JvmField
val MyToolWindow = IconLoader.getIcon("/icons/myToolWindow.png", javaClass)
}The getIcon() method of IconLoader (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/openapi/util/IconLoader.kt) can be used to access the icons. The path to the icon passed in as argument to IconLoader.getIcon() must start with leading /.
Starting with 2021.2, *Icons class is not required to be located in icons package but can use plugin's package: icons.MyIcons → com.example.plugin.MyIcons.
Icons defined inside plugin.xml with icon attribute for <action> ("action" in "Plugin Configuration File") or extension point, as well in @Presentation's icon attribute, can be referenced in two ways:
by icon file path
by icon constant in the icons holder class
To reference an icon by path, provide the path relative to the resources directory, e.g., for icons located in my-plugin/src/main/resources/icons directory:
<actions>
<action icon="/icons/myAction.svg" ... />
</actions>
<extensions defaultExtensionNs="com.intellij">
<toolWindow icon="/icons/myToolWindow.svg" ... />
</extensions>In case of icons holder class, reference the icon constants. Note that if the class is located in the top-level icons package, name icons will be automatically prefixed and must not be specified. In case of placing the class in a custom package, the full package name must be provided, e.g.:
<actions>
<!-- referencing icons from class in top-level 'icons' package -->
<action icon="MyIcons.MyAction" ... />
</actions>
<extensions defaultExtensionNs="com.intellij">
<!-- referencing icons from custom package -->
<toolWindow icon="com.example.plugin.MyIcons.MyToolWindow" ... />
</extensions>IntelliJ Platform supports Retina displays and has a bundled dark theme called Darcula (https://www.jetbrains.com/help/idea/user-interface-themes.html). Thus, every icon should have a dedicated variant for Retina devices and Darcula theme. In some cases, you can skip dark variants if the original icon looks good under Darcula.
Required icon sizes depend on the usage as listed in the following table:
Usage | Icon Size (pixels) |
|---|---|
Node, Action, Filetype | 16x16 |
Tool window | 13x13 |
Editor gutter | 12x12 |
Editor gutter (New UI) | 14x14 |
SVG (Scalable Vector Graphics (https://en.wikipedia.org/wiki/Scalable_Vector_Graphics)) icons are supported since 2018.2.
As SVG icons can be scaled arbitrarily, they provide better results in HiDPI environments or when used in combination with bigger screen fonts (e.g., in presentation mode).
A base size denoting the size (in the user space) of the rendered image in 1x scale should be provided. The size is set via the width and height attributes omitting the size units. If unspecified, it defaults to 16x16 pixels.
A minimal SVG icon file:
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<rect width="100%" height="100%" fill="green"/>
</svg>The naming notation used for PNG icons (see below) is still relevant.
However, the @2x version of an SVG icon should still provide the same base size. The icon graphics of such an icon can be expressed in more details via double precision. If the icon graphics are simple enough so that it renders perfectly in every scale, then the @2x version can be omitted.
Use SVG icons for if your plugin targets 2018.2+.
All icon files must be placed in the same directory following this naming pattern (replace .png with .svg for SVG icons):
Theme/Resolution | File name pattern | Size |
|---|---|---|
Default | iconName.png | W x H |
Darcula | iconName_dark.png | W x H |
Default + Retina | iconName@2x.png | 2*W x 2*H |
Darcula + Retina | iconName@2x_dark.png | 2*W x 2*H |
The IconLoader class will load the icon that matches the best depending on the current environment.
Here are examples of toolWindowStructure.png icon representations:
Theme/Resolution | File name | Icon |
|---|---|---|
Default | toolWindowStructure.png |
|
Darcula | toolWindowStructure_dark.png |
|
Default + Retina | toolWindowStructure@2x.png |
|
Darcula + Retina | toolWindowStructure@2x_dark.png |
|
Animated icons are a way to show that plugin is now performing some long-time action. For example, when plugin is loading some data.
Any animated icon is a set of frames that loop with some delay.
To create a new animated icon, use the AnimatedIcon (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ui/AnimatedIcon.java). If you want to create an icon where frames follow each other with the same delay, use a constructor that accepts a delay and icons:
AnimatedIcon icon = new AnimatedIcon(
500,
AllIcons.Ide.Macro.Recording_1,
AllIcons.Ide.Macro.Recording_2);To create an icon from frames with different delays, use AnimatedIcon.Frame. Each frame represents an icon, and a delay until the next frame.
If you want to show somewhere that there is a long process, you can use the predefined AnimatedIcon.Default loader icon. This icon has a larger AnimatedIcon.Big version.
Register resource bundle via com.intellij.iconDescriptionBundle extension point to provide tooltips automatically for all SimpleColoredComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java) renderers.
Create icon.<icon-path>.tooltip key in given resource bundle, where <icon-path> is the icon path with leading slash and .svg removed and slashes replaced with dots (e.g., /nodes/class.svg → icon.nodes.class.tooltip).
This feature is available since 2022.3.
See New UI Icons Guide (https://www.figma.com/community/file/1227729570033544559) for guidelines and overview.
To fully support the New UI (https://www.jetbrains.com/help/idea/new-ui.html), the plugin must provide additional dedicated icons and mapping information. This allows supporting both UI variants at the same time, depending on what the user has selected.
Create a new expui folder in your icon root folder (Reference).
Copy all icons for the New UI into this folder.
Create an empty $PluginName$IconMappings.json mapping file in the resources root folder.
Register $PluginName$IconMappings.json in plugin.xml via the com.intellij.iconMapper extension point.
Sample setup from Maven plugin:
Icon resources root folder: images (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/resources/images)
Mapping file: MavenIconMappings.json (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/resources/MavenIconMappings.json)
Extension point registration (<iconMapper mappingFile="MavenIconMappings.json"/>): plugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/resources/META-INF/plugin.xml)
All New UI icons must be mapped in the $PluginName$IconMappings.json mapping file.
For each New UI icon, add a mapping entry inside expui block. Each folder starts a new block containing all its entries (see linked MavenIconMappins.json sample from above).
In this example, the icon root folder is named icons:
{
"icons": {
"expui": {
"folderName": {
"icon1.svg": "icons/icon1.svg",
"icon2.svg": "icons/icon2.svg"
},
"anotherFolder": {
"anotherIcon.svg": "images/anotherIcon.svg"
}
}
}
}If one new icon replaces several old icons, use a JSON list. Example from PlatformIconMappings.json (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/icons/src/PlatformIconMappings.json):
"vcs.svg": [
"toolwindows/toolWindowChanges.svg",
"vcs/branch.svg"
]The New UI uses outlined icons for tool windows that have a size of 20x20 pixels and 16x16 pixels in compact mode (https://www.jetbrains.com/help/idea/new-ui.html#compact-mode). Plugin developers who want to provide all necessary variants of their tool window icons use the following suffix scheme for their icon filename, here referred to as iconToolWindow:
iconToolWindow.svg: a 16x16 pixels compact mode variant of the icon for the light theme.
iconToolWindow_dark.svg: a 16x16 pixels compact mode variant of the icon for the dark theme.
iconToolWindow@20x20.svg: a 20x20 pixels variant of the icon for the light theme.
iconToolWindow@20x20_dark.svg: a 20x20 pixels variant of the icon for the dark theme.
To work as expected, the New UI requires specific colors for icon content. This is necessary for situations where tool window buttons are active, during which the background is highlighted. To enhance contrast, the IntelliJ Platform dynamically alters the icon's content color to white.
Hence, for the creation of light and dark mode variants, plugin authors must use the following prescribed colors within their icons:
Theme | Color Code |
|---|---|
Light | #6C707E ▆ |
Dark | #CED0D6 ▆ |
Various online resources, like the IntelliJ Platform UI Guidelines here (https://jetbrains.design/intellij/components/tool_window/#07) and here (https://jetbrains.design/intellij/principles/icons/#grid-and-size), will be updated soon and currently don't include information about the New UI.
Inspecting existing UIUse UI Inspector (Internal Actions - UI Inspector) to locate the underlying Swing component implementation or to inspect an existing UI at runtime.
Package com.intellij.ui (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/)
Package com.intellij.util.ui (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/)
Always use JBColor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/JBColor.java) instead of plain java.awt.Color (highlighted via inspection Plugin DevKit | Code | Use Darcula aware JBColor). Custom colors must be retrieved via JBColor.namedColor() set by the current Theme (Getting Started). See Exposing Theme Metadata on how to expose corresponding metadata.
If it's needed to retrieve a color from one place and use it in another, do not just retrieve once and use the retrieved value. Instead, use JBColor.lazy() and pass in a lambda expression to retrieve the color. This lambda expression needs to be fast and safe enough, as it will be called every time the color is retrieved, for example, for painting. Following this approach ensures that the color will be properly updated if it's changed at the source, for example, due to a theme or scheme change.
Generic UI colors (e.g., for drawing borders) can be accessed via UIUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/UIUtil.java) and JBUI (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/JBUI.java). A number of hardcoded colors is defined in JBColor, Gray (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/Gray.java), and LightColors (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/LightColors.java)
ColorUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/ColorUtil.java) allows tuning existing colors.
Use NaturalComparator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/base/src/com/intellij/openapi/util/text/NaturalComparator.java) for "natural" sorting of items.
StringUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/text/StringUtil.java) contains a number of useful methods for manipulating text for UI usage:
unpluralize()/pluralize() using English rules
formatDuration() to format duration: 2 m 3 s 456 ms
formatFileSize() to format filesize: 1.23 KB
escapeLineBreak() and related methods to escape special characters
shortenTextWithEllipsis() and shortenPathWithEllipsis() to produce abbreviated UI texts ending with '…'
quote() and unquoteString() to wrap values: Usages of "$value$": 218 found
See Internationalization for information about internationalizing plugins.
See NlsMessages (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core-impl/src/com/intellij/ide/nls/NlsMessages.java) to produce localized messages.
To store and retrieve values for Recently Used entries (e.g., filter values), use RecentsManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ui/RecentsManager.java).
To determine the current Theme (Getting Started)'s style, use JBColor.isBright() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/JBColor.java).
Always create borders and insets via factory methods from JBUI.Borders (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/JBUI.java) and JBUI.Insets (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/util/ui/JBUI.java), which create DPI-aware instances. Using standard DPI-agnostic instances (reported by inspection Plugin DevKit | Code | Use DPI-aware borders and Plugin DevKit | Code | Use DPI-aware insets) can result in UI layout problems.
If you use DPI-aware insets in an empty border (JBUI.Borders.empty()), then the insets will be updated automatically, for example, if scaling is changed because the IDE Zoom action was performed or for any other reason. If you use the insets elsewhere, you need to manually call JBInsets.update() in your component's updateUI() method to update the insets accordingly.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
JCEF is available since 2020.1 as an experimental feature and is enabled by default since 2020.2.
JCEF (Java Chromium Embedded Framework) is a Java port of CEF (https://bitbucket.org/chromiumembedded/cef/wiki/Home). It allows for embedding Chromium-based browsers (https://www.chromium.org/Home) in Swing applications.
Embedding of the browser component inside the IDE can be used for:
rendering HTML content
previewing generated HTML (e.g., from Markdown)
creating custom web-based components (e.g., diagrams preview, image browser, etc.)
It is recommended to implement UI in the default IntelliJ Platform UI framework, which is Swing. Consider using JCEF approach only in cases, when a plugin needs to display HTML documents or the standard approach for creating UI is insufficient.
JCEF replaces JavaFX, which was used to render web content in IDEs in the past.
Using JavaFXUsing JavaFX in 3rd party plugins has been deprecated since 2020.2. To continue using JavaFX in 2020.2 or later, add an explicit dependency (Plugin Dependencies) on JavaFX Runtime for Plugins (https://plugins.jetbrains.com/plugin/14250-javafx-runtime-for-plugins) (not recommended).
See JavaFX and JCEF in the IntelliJ Platform (https://blog.jetbrains.com/platform/2020/07/javafx-and-jcef-in-the-intellij-platform/) blog post for the details.
JCEF is available and enabled by default since 2020.2. No additional actions are required.
Using JCEF requires using a dedicated JetBrains Runtime and enabling JCEF in the IDE Registry.
Go to the JetBrains Runtime releases list (https://github.com/JetBrains/JetBrainsRuntime/releases).
Download "Binaries for launching IntelliJ IDEA" matching your operating system, e.g., jbr_jcef-17.0.9-osx-x64-b1087.7.tar.gz for macOS.
Unpack the archive.
Follow the steps described in the IDEA Web Help (https://www.jetbrains.com/help/idea/2020.2/switching-boot-jdk.html) and choose the downloaded JBR.
Invoke Help | Find Action..., type "Registry", and press enter to open the Registry dialog.
Enable the ide.browser.jcef.enabled flag.
Restart the IDE for changes to take effect.
The core JCEF class exposed by IntelliJ Platform API is JBCefApp (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefApp.java). It is responsible for initializing JCEF context and managing its lifecycle.
There is no need for initializing JBCefApp explicitly. It is done when JBCefApp.getInstance() is called, or when browser or client objects are created.
Before using JCEF API, it is required to check whether JCEF is supported in the running IDE. It is done by calling JBCefApp.isSupported():
if (JBCefApp.isSupported()) {
// use JCEF
} else {
// optional fallback to an alternative browser-less solution
}JCEF can be unsupported when:
The IDE is started with an alternative JDK that does not include JCEF.
Its version is not compatible with the running IDE.
JCEF browser is represented by JBCefBrowser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowser.java) class. It is responsible for loading and rendering requested documents in the actual Chromium-based browser.
JCEF browsers can be created either by using the JBCefBrowser class' constructors, or via JBCefBrowserBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowserBuilder.java). Use constructors in the cases when a browser with the default client and default options is enough. The builder approach allows using custom clients and configuring other options.
JBCefBrowser.getComponent() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowser.java) exposes the UI component embedding the actual browser. The component is an instance of Swing JComponent, which can be added to the plugin UI:
// assume 'JPanel myPanel' is a part of a tool window UI
JBCefBrowser browser = new JBCefBrowser();
myPanel.add(browser.getComponent());To load a document in the browser, use one of JBCefBrowserBase.load*() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefBrowserBase.java) methods. Methods loading documents can be called from both EDT and background threads. It is possible to set an initial URL (passed to constructor or builder) that will be loaded when browser is created and initialized.
Browser client provides an interface for setting up handlers related to various browser events, e.g.:
HTML document loaded
console message printed
browser gained focus
Handlers allow reacting to these events in plugin code and change browser's behavior. Each browser is tied to a single client and a single client can be shared with multiple browser instances.
Browser client is represented by JBCefClient (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefClient.java), which is a wrapper for JCEF's CefClient (https://github.com/JetBrains/jcef/tree/master/java/org/cef/CefClient.java). JBCefClient allows registering multiple handlers of the same type, which is not possible with CefClient. To access the underlying CefClient and its API, call JBCefClient.getCefClient().
If a JBCefBrowser instance is created without passing a specific client, it is tied to a default client created implicitly. Implicit clients are disposed automatically, following the associated browser instance disposal.
For more advanced use cases, create a custom client by calling JBCefApp.createClient() and register required handlers. Custom clients must be disposed explicitly in the plugin code.
To access the client associated with a browser, call JBCefBrowser.getJBCefClient().
JCEF API provides various event handler interfaces that allows handling a wide set of events emitted by the browser. Example handlers:
CefLoadHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefLoadHandler.java) - handles browser loading events. Example: Implement CefLoadHandler.onLoadEnd() to execute scripts after document is loaded.
CefDisplayHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefDisplayHandler.java) - handles events related to browser display state. Example: Implement CefDisplayHandler.onAddressChange() to load project files in the browser when a local file link is clicked, or opening an external browser if an external link is clicked.
CefContextMenuHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefContextMenuHandler.java) - handles context menu events. Example: Implement CefContextMenuHandler.onBeforeContextMenu() to change the items of the browser context menu.
CefDownloadHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefDownloadHandler.java) - file download events. Example: Implement CefDownloadHandler.onBeforeDownload() to enable downloading files in the embedded browser.
See org.cef.handler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler) package for all available handlers.
For each handler interface, JCEF API provides an adapter class, which can be extended to avoid implementing unused methods, e.g., CefLoadHandlerAdapter (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefLoadHandlerAdapter.java).
Handlers should be registered with JBCefClient.getCefClient().add*Handler() methods.
Please note that JBCefClient exposes methods for adding handlers, but it is not recommended to use them.
JCEF API allows executing JavaScript code in the embedded browser from the plugin code. JavaScript can be used for manipulating DOM, creating functions required in implemented features, injecting styles, etc.
In the simplest case, JavaScript code can be executed by using JBCefBrowser.getCefBrowser().executeJavaScript(), e.g.:
browser.getCefBrowser().executeJavaScript(
"alert('Hello World!')",
url, lineNumber
);The above snippet will be executed in the embedded browser and will display alert box with the "Hello World!" message. The url and lineNumber parameters are used in the error report in the browser, if the script throws an error. Their purpose is to help debugging in case of errors, and they are not crucial for the script execution. It is common to pass browser.getCefBrowser().getUrl() or null/empty string, and 0 as these parameters.
JCEF doesn't provide direct access to DOM from the plugin code (it may change (https://youtrack.jetbrains.com/issue/JBR-2046) in the future) and asynchronous communication with JavaScript is achieved with the callback mechanism. It allows executing plugin code from the embedded browser via JavaScript, e.g., when a button or link is clicked, a shortcut is pressed, a JavaScript function is called, etc.
JavaScript query callback is represented by JBCefJSQuery (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefJSQuery.java). It is an object bound to a specific browser, and it holds a set of handlers that implement the required plugin behavior.
Consider a case, which requires opening local files links in the editor and external links in an external browser. Such a requirement could be implemented as follows (each step is explained under the code snippet):
JBCefJSQuery openLinkQuery = JBCefJSQuery.create(browser); // 1
openLinkQuery.addHandler((link) -> { // 2
if (LinkUtil.isExternal(link)) {
BrowserUtil.browse(link);
} else {
EditorUtil.openFileInEditor(link);
}
return null; // 3
});
browser.getCefBrowser().executeJavaScript( // 4
"window.openLink = function(link) {" +
openLinkQuery.inject("link") + // 5
"};",
browser.getCefBrowser().getURL(), 0
);
browser.getCefBrowser().executeJavaScript( // 6
"""
document.addEventListener('click', function (e) {
const link = e.target.closest('a').href;
if (link) {
window.openLink(link);
}
});""",
browser.getCefBrowser().getURL(), 0
);Create JBCefQuery instance. Make sure that the passed browser instance is of type JBCefBrowserBase (casting may be needed).
Add a handler implementing a plugin code to be executed. Example implementation opens a link in the editor or an external browser depending on whether the link is local or external.
Handlers can optionally return JBCefJSQuery.Response object, which holds information about success or error occurred on the plugin code side. It can be handled in the browser if needed.
Execute JavaScript, which creates a custom openLink function.
Inject JavaScript code responsible for invoking plugin code implemented in step 2. The handler added to openLinkQuery will be invoked on each openLink function call.
Note the "link" parameter of the JBCefJSQuery.inject() method. It is the name of the openLink function's link parameter. This value is injected to the query function call, and can be any value that is required by handler, e.g., "myJsObject.prop", "'JavaScript string'", etc.
Execute JavaScript, which registers a click event listener in the browser. Whenever an a element is clicked in the browser, the listener will invoke the openLink function defined in step 4 with the href value of the clicked link.
In cases when a plugin feature implements a web-based UI, the plugin may provide HTML, CSS, and JavaScript files in its distribution (Plugin Content) or build them on the fly depending on some configuration. Such resources cannot be easily accessed by the browser. They can be made accessible by implementing proper request handlers, which make them available to the browser at predefined URLs.
This approach requires implementing CefRequestHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefRequestHandler.java), and CefResourceRequestHandler (https://github.com/JetBrains/jcef/tree/master/java/org/cef/handler/CefResourceRequestHandler.java), which map resource paths to resource providers.
Serving such resources is implemented by the Image Viewer component responsible for displaying SVG files in IntelliJ Platform-based IDEs. See JCefImageViewer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/images/src/org/intellij/images/editor/impl/jcef/JCefImageViewer.kt) and related classes for the implementation details.
Default browser scrollbars may be insufficient, e.g. when they stand out of the IDE scrollbars look, or specific look and behavior is required.
In JCEF browsers, scrollbars look and feel can be customized by CSS and JavaScript. IntelliJ Platform provides JBCefScrollbarsHelper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefScrollbarsHelper.java) that allows to customize scrollbars in two ways:
Using JBCefScrollbarsHelper.getOverlayScrollbarStyle(), which provides the styles adapted to the IDE scrollbars.
Using OverlayScrollbars (https://kingsora.github.io/OverlayScrollbars/) library adapted to the IDE look and feel. For the details, see getOverlayScrollbarsSourceCSS(), getOverlayScrollbarsSourceJS(), and buildScrollbarsStyle() Javadocs. It should be used when transparent scrollbars or other advanced options are required.
JBCefBrowser, JBCefClient, and JBCefJSQuery classes implement JBCefDisposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefDisposable.java), which extends Disposable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/Disposable.java). It means that these classes should clean up their resources according to the rules described on the Disposer and Disposable page.
For example, a custom JBCefClient with registered handlers should remove them in the dispose() method implementation.
See JBCefTestHelper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-tests/testSrc/com/intellij/ui/jcef/JBCefTestHelper.java) and tests in that package.
The Chrome DevTools (https://developers.google.com/web/tools/chrome-devtools/), embedded into JCEF, can be used as a debugging and profiling tool. It is active by default, so that a Chrome DevTools client can attach to it via the default 9222 port. Default port can be changed via the registry key ide.browser.jcef.debug.port (go to Help | Find Action... and type "Registry").
JavaScript debugger in IntelliJ IDEA Ultimate can thus be used to debug JavaScript code running in the IDE via the Chrome DevTools. Use the Attach to Node.js/Chrome (https://www.jetbrains.com/help/idea/run-debug-configuration-node-js-remote-debug.html) configuration with a proper port number.
Also, JCEF provides a default Chrome DevTools frontend (similar to the one in the Chrome browser) that can be opened from the JCEF's browser component context menu via Open DevTools. The menu item is available in the internal mode (Enabling Internal Mode) only, and since version 2021.3, the registry key ide.browser.jcef.contextMenu.devTools.enabled must be set to true explicitly.
To access the Chrome DevTools in the plugin code, use the following API:
CefBrowser devTools = browser.getCefBrowser().getDevTools();
JBCefBrowser devToolsBrowser = JBCefBrowser.createBuilder()
.setCefBrowser(devTools)
.setClient(browser.getJBCefClient())
.build();In order to open DevTools in a separate window, call JBCefBrowser.openDevtools().
Markdown preview panel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/src/org/intellij/plugins/markdown/ui/preview/jcef/MarkdownJCEFHtmlPanel.kt)
SVG Image Viewer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/images/src/org/intellij/images/editor/impl/jcef/JCefImageViewer.kt)
PDF Viewer (https://github.com/FirstTimeInForever/intellij-pdf-viewer) plugin
CodeStream (https://github.com/TeamCodeStream/codestream) plugin
Excalidraw Integration (https://github.com/bric3/excalidraw-jetbrains-plugin) plugin
Creating IntelliJ plugin with WebView (https://medium.com/virtuslab/creating-intellij-plugin-with-webview-3b27c3f87aea) blog post
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Color scheme management in IntelliJ IDEA 12.1 was changed to ease scheme designers' work and make schemes look equally well for different programming languages even if not designed specifically for these languages. Previously, language plugins were using fixed default colors incompatible, for example, with dark schemes.
The new implementation allows specifying a dependency on a set of standard text attributes linked to a scheme but not to any specific language. Language-specific attributes still can be set by a scheme designer if needed, but it's optional. New color schemes have got a new .icls (Idea CoLor Scheme) extension to avoid confusion about compatibility problems with older platform versions: if only standard attributes are set, they will not be used by the version before 12.1, resulting in different highlighting colors.
The easiest and the best way to specify highlighting text attributes is to specify a dependency on one of standard keys defined in DefaultLanguageHighlighterColors (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/DefaultLanguageHighlighterColors.java):
static final TextAttributesKey MY_KEYWORD =
TextAttributesKey.createTextAttributesKey("MY_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD);The color scheme manager will search first for text attributes specified by the MY_KEYWORD key. If those are not defined explicitly or if all the attributes are empty (undefined), it will search them using the DEFAULT_KEYWORD key. If neither is defined, it will further fall back to a default scheme.
Text attribute keys can be chained, for example, you can define another key as:
static final TextAttributesKey MY_PREDEFINED_SYMBOL =
TextAttributesKey.createTextAttributesKey("MY_PREDEFINED_SYMBOL", MY_KEYWORD);The rule is the same: if text attributes can not be found by the MY_PREDEFINED_SYMBOL key or are empty, the color scheme manager will search for MY_KEYWORD and if not found (empty) will further look for DEFAULT_KEYWORD.
A use of fixed default attributes is strongly discouraged.
If you are unsure which base key to use, it's better to pick the most generic one, for example, DefaultLanguageHighlighterColors.IDENTIFIER. Remember that using fixed default attributes will force a scheme designer to explicitly set up a color for this element. Otherwise, its default colors may visually conflict with a color scheme. If the scheme designer doesn't have a language plugin, he will not be able to fix this at all.
A language plugin may provide default text attributes for "Default" and "Darcula" bundled schemes or basically for any other scheme if the scheme's name is known. This can be done in plugin.xml (Plugin Configuration File) by adding an com.intellij.additionalTextAttributes extension providing the name of the file containing desired text attributes, for example:
<extensions defaultExtensionNs="com.intellij">
...
<additionalTextAttributes
scheme="Default"
file="colorSchemes/MyLangDefault.xml"/>
...
</extensions>It tells the IDE that the file MyLangDefault.xml must be searched in resources under colorSchemes. Note that the path should not start with a backslash and its fully qualified name (in our case colorSchemes/MyLangDefault.xml) MUST BE UNIQUE to avoid naming collisions between different providers. Thus, adding a language prefix, for example, "MyLang", is highly recommended.
The file itself is an extract from a color scheme with required attributes, for example:
<?xml version='1.0'?>
<list>
<option name="MY_VAR">
<value>
<option name="FOREGROUND" value="660000"/>
</value>
</option>
<option name="MY_SPECIAL_CHAR">
<value>
<option name="FOREGROUND" value="008000"/>
<option name="BACKGROUND" value="e3fcff"/>
<option name="FONT_TYPE" value="1"/>
</value>
</option>
</list>Note: When the scheme is copied via Save as... all its attributes, including the ones defined in the extension, will be copied to the new scheme. A scheme designer may need to check that these copied attributes do not conflict with their color scheme, although in this case, the plugin is installed, and it should not cause any problems. Anyway, try to stick with a simple key dependency if possible (note that it works well for "Darcula") and provide explicit attributes only if necessary.
Choose a scheme which will be used as a base, for example, "Default".
Click Save As... and give a name for the new scheme.
First set attributes in the General section and proceed with Language Defaults.
Check all the languages and adjust language-specific text attributes if necessary. In most cases, this may not be needed, but two cases may require an extra action:
There is an obsolete plugin which does not use the new color scheme management API and therefore does not utilize the attributes set in Language Defaults. Ideally, a report must be created for the language plugin so that its author will fix it eventually.
A plugin intentionally sets some default colors and, if the scheme was created from a default one, the colors are copied to the newly created scheme. This can be fixed either by resetting all the attributes to restore the inheritance from Language Defaults (see below) or by setting other colors suitable for the scheme.
The first way is preferable since it will require less effort to change the color scheme later.
For many language text attributes that do not have any values, there will be a line indicating that the attributes are inherited from a specific section/attributes, such as Keyword in Language Defaults. If an element has any attributes set, only these attributes are used. All attributes from the base element are ignored. To restore the inheritance, uncheck all the boxes, and click Apply.
All available UI Customization Keys that can be used in Custom Themes (Customizing Themes - Icons and UI Controls) must be defined in a dedicated *.themeMetadata.json file which is registered via com.intellij.themeMetadataProvider extension point.
The following minimal sample demonstrates all details required when exposing UI customization keys of your plugin's UI.
/resources/META-INF/plugin.xml:
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<themeMetadataProvider path="/META-INF/MyPlugin.themeMetadata.json"/>
</extensions>
</idea-plugin>/resources/META-INF/MyPlugin.themeMetadata.json:
{
"name": "My Plugin",
"fixed": false,
"ui": [
{
"key": "MyComponent.border",
"description": "The border for my component. Not used anymore.",
"deprecated": true,
"source": "com.example.MyComponent"
},
{
[more keys...]
}
]
}name - Human-readable name, e.g., plugin name
fixed - Specifies whether metadata describes external elements, e.g., an UI library. Default: false.
ui - Root element listing all customization keys:
key - Customization key name (see Key Naming Scheme)
description - Description to be shown to Theme authors editing *.theme.json files
deprecated - true when the key is deprecated. It is highly recommended to provide explanation and/or replacement in description if available.
source - Fully qualified name of the underlying UI component implementation, e.g., javax.swing.JPasswordField
since - The release number when this UI customization key was exposed, e.g., 2021.1
Note: The since attribute is supported starting with the 2019.2 release and it is not displayed in versions prior to 2019.2.
It is highly recommended to always provide a description entry, so Theme authors can understand usages.
Do not remove existing keys, but deprecate them instead to help Theme authors upgrade their existing themes.
Color keys can be used via JBColor.namedColor() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/ui/src/com/intellij/ui/JBColor.java) providing defaults for Light and Dark theme:
private static final JBColor SECTION_HEADER_FOREGROUND =
JBColor.namedColor(
"Plugins.SectionHeader.foreground",
new JBColor(0x787878,0x999999)
);Other keys can be obtained via javax.swing.UIManager#getXXX() methods.
All keys must follow this Naming Pattern:
Object[.SubObject].[state][Part]Property

Word | Use for | Example |
|---|---|---|
foreground | Text color. | Label.foreground |
background | Background color for objects with text. | Label.background |
<part>Color | Objects with a single color (do not have foreground/background). Do not use the word "Color" separately, always use with the "part" word. The word "Color" shows that this is a color property. Otherwise, it can be confused with a property of another type. | Popup.borderColor Group.separatorColor |
Word | Use for | Example |
|---|---|---|
Active | Enabled components, default state. Omit this word. The default state does not need explicit naming. | Notification.background |
Inactive | Enabled components that might be perceived as interactive but are actually not. Example: a tree with visible selection but not in focus. Goes after other state words. | Tree.inactiveBackground ToolWindow.HeaderTab.hoverInactiveBackground |
Focused | The current focused component. | Button.focusedBorderColor |
Selected | A selected tab or any other control that has equally meaningful selected and inactive states. | ToolWindow.HeaderTab.selectedBackground |
Hover Pressed | An action as indicated in states. | Link.hoverForeground Link.pressedForeground |
Error Warning Success | Validation states. See example (https://jetbrains.design/intellij/principles/validation_errors/) in the guide article. | ValidationTooltip.errorBackground ValidationTooltip.warningBorderColor |
Disabled | Unavailable components. | Label.disabledForeground |
A part is an internal element of a component, e.g., an arrow button in a combo box. Create a separate key for a part if its properties differ from the parent object.
If a part is common among several components, use the same name for it. Notable examples of common parts:
Common parts | Use for | Example |
|---|---|---|
Accelerator Shortcut | Shortcut foreground. | Menu.acceleratorForeground Editor.shortcutForeground |
Border | A line around a component. | NavBar.borderColor |
Caret | The vertical line that denotes typing place. | TextField.caretForeground |
ModifiedItem | An object that has been modified but not yet saved. Example: change anything in the Settings dialog, the setting group name in the tree becomes blue. | Tree.modifiedItemForeground |
Focus | Wide focus border around a component. | Component.focusColor "Component" is a special key that sets common properties for several basic input components. |
Info | Secondary labels with additional useful information. Usually appear in gray color to the right or below a regular label. | CompletionPopup.infoForeground |
Icon | An icon that is created with a source code (not an image file). | Table.sortIconColor |
Selection | The focus place in a component with selectable text. Can be in a typed text or in a list or tree. Goes before other state words (for historical reasons). | TextField.selectionForeground Tree.selectionInactiveBackground |
Separator | A horizontal or vertical line inside a component. Can be with a label. | Menu.separatorColor |
Shadow | A shadow below a component. | Button.shadowColor |
Use a subobject when creating keys for one of the following:
An implementation variation. Usually has a similar set of UI property keys as the parent object. Examples:
Default button: Button.Default.background
Tool window notification: Notification.ToolWindow.errorBackground
An internal smaller component of a complex component with its own UI and behavior. Examples:
Tool window tab: ToolWindow.HeaderTab.inactiveBackground
The hint text at the bottom of a popup: Popup.Advertiser.background
If a component has a gradient color, add the words "start" and "end" for the beginning and ending of a gradient. Examples:
Button.startBorderColor/Button.endBorderColor
SearchMatch.startBackground/SearchMatch.endBackground
Capitalize Object and SubObject. Use lowerCamelCase for property.
Do not use | Use instead |
|---|---|
Color as a separate word | <Part>Color |
Outline | borderColor |
Text | Foreground |
darcula and other look-and-feel names | Omit |
Some color keys are not named according to the rules above. Such keys are inherited from Java Swing and cannot be renamed for compatibility reasons. Do not use naming patterns from the legacy keys.
Examples of Swing keys:
activeCaption Correct: WindowsDialogHeader.background
Button.disabledText Correct: Button.disabledForeground
TableHeader.background Correct: Table.Header.background
This section is relevant for IntelliJ Platform developers only.
Metadata is split up as follows:
IntelliJPlatform.themeMetadata.json (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/metadata/IntelliJPlatform.themeMetadata.json) - all keys from IntelliJ Platform and custom UI components
JDK.themeMetadata.json (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/metadata/JDK.themeMetadata.json) - all keys from Swing components
New keys should be added to IntelliJPlatform.themeMetadata.json only (or corresponding "local" *.themeMetadata.json file of the plugin if applicable).
Please make sure to add a description and use since and deprecated attributes explained in Attributes. Respect Key Naming Scheme and keep alphabetical ordering of keys.
Product Help: Menus and toolbars (https://www.jetbrains.com/help/idea/customize-actions-menus-and-toolbars.html)
Platform UI Guidelines: Toolbar (https://jetbrains.design/intellij/controls/toolbar/)
The actions system allows plugins to add their items to IntelliJ Platform-based IDE menus and toolbars. For example, one of the action classes is responsible for the File | Open File... menu item and the Open... toolbar button.
Actions in the IntelliJ Platform require a code implementation and must be registered. The action implementation determines the contexts in which an action is available, and its functionality when selected in the UI. Registration determines where an action appears in the IDE UI. Once implemented and registered, an action receives callbacks from the IntelliJ Platform in response to user gestures.
The Creating Actions tutorial describes the process of adding a custom action to a plugin. The Grouping Actions tutorial demonstrates three types of groups that can contain actions.
An action is a class derived from the abstract class AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java). For actions available during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"), extend DumbAwareAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/project/DumbAwareAction.java). See also below.
The IntelliJ Platform calls methods of actions when a user interacts with a menu item or toolbar button.
No fields allowedClasses based on AnAction must not have class fields of any kind. This is because an instance of AnAction class exists for the entire lifetime of the application. If the AnAction class uses a field to store data that has a shorter lifetime and doesn't clear this data promptly, the data leaks. For example, any AnAction data that exists only within the context of a Project causes the Project to be kept in memory after the user has closed it.
Every IntelliJ Platform action should override AnAction.update() and must override AnAction.actionPerformed().
An action's method AnAction.update() is called by the IntelliJ Platform framework to update an action state. The state (enabled, visible) of an action determines whether the action is available in the UI. An object of the AnActionEvent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnActionEvent.java) type is passed to this method and contains information about the current context for the action. Actions are made available by changing state in the Presentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java) object associated with the event context. As explained in Overriding the AnAction.update() Method, it is vital update() methods execute quickly and return execution to platform.
AnAction.getActionUpdateThread() (2022.3+) return an ActionUpdateThread (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionUpdateThread.java), which specifies if the update() method is called on a background thread (BGT) or the event-dispatching thread (EDT) (General Threading Rules). The preferred method is to run the update on the BGT, which has the advantage of guaranteeing application-wide read access to PSI (Program Structure Interface (PSI)), the virtual file system (Virtual File System) (VFS), or project models (Project Structure). Actions that run the update session on the BGT should not access the Swing component hierarchy directly. Conversely, actions that specify to run their update on EDT must not access PSI, VFS, or project data but have access to Swing components and other UI models. All accessible data is provided by the DataContext as explained in . When switching from BGT to EDT is absolutely necessary, actions can use AnActionEvent.getUpdateSession() to access the UpdateSession (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/UpdateSession.java) and then call UpdateSession.compute() to run a function on EDT. Inspection Plugin DevKit | Code | ActionUpdateThread is missing highlights missing implementation of AnAction.getActionUpdateThread() (2022.3+).
An action's method AnAction.actionPerformed() is called by the IntelliJ Platform if available and selected by the user. This method does the heavy lifting for the action: it contains the code executed when the action gets invoked. The actionPerformed() method also receives AnActionEvent as a parameter, which is used to access any context data like projects, files, selection, etc. See Overriding the AnAction.actionPerformed() Method for more information.
There are other methods to override in the AnAction class, such as changing the default Presentation object for the action. There is also a use case for overriding action constructors when registering them with dynamic action groups, demonstrated in the Grouping Actions ("Adding Child Actions to the Dynamic Group" in "Grouping Actions") tutorial.
The method AnAction.update() is periodically called by the IntelliJ Platform in response to user gestures. The update() method gives an action to evaluate the current context and enable or disable its functionality. Implementors must ensure that changing presentation and availability status handles all variants and state transitions; otherwise, the given Action will get "stuck".
PerformanceThe AnAction.update() method can be called frequently and on EDT. It must execute very quickly; no real work must be performed. For example, checking selection in a tree or a list is considered valid, but working with the file system is not.
If the new state of an action cannot be determined quickly, then evaluation should be performed in the AnAction.actionPerformed() method, and notify (Notifications) the user that the action cannot be executed if the context isn't suitable.
The AnActionEvent object passed to update() carries information about the current context for the action. Context information is available from the methods of AnActionEvent, providing information such as the Presentation and whether the action is triggered by a Toolbar. Additional context information is available using the method AnActionEvent.getData(). Keys defined e.g. in CommonDataKeys (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/CommonDataKeys.java) are passed to the getData() method to retrieve objects such as Project, Editor, PsiFile, and other information. Accessing this information is relatively light-weight and is suited for AnAction.update().
Based on information about the action context, the AnAction.update() method can enable, disable, or hide an action. An action's enabled/disabled state and visibility are set using methods of the Presentation object, which is accessed using AnActionEvent.getPresentation().
The default Presentation object is a set of descriptive information about a menu or toolbar action. Every context for an action - it might appear in multiple menus, toolbars, or Navigation search locations - has a unique presentation. Attributes such as an action's text, description, and icons and visibility and enable/disable state, are stored in the presentation. The attributes in a presentation get initialized from the action registration. However, some can be changed at runtime using the methods of the Presentation object associated with an action.
The enabled/disabled state of an action is set using Presentation.setEnabled(). The visibility state of an action is set using Presentation.setVisible(). If an action is enabled, the AnAction.actionPerformed() can be called if a user selects an action in the IDE. A menu action shows in the UI location specified in its registration. A toolbar action displays its enabled (or selected) icon, depending on the user interaction.
When an action is disabled AnAction.actionPerformed() will not be called. Toolbar actions display their respective icons for the disabled state. The visibility of a disabled action in a menu depends on whether the host menu (e.g., "ToolsMenu") containing the action has the compact attribute set. See Grouping Actions for more information about the compact attribute and menu actions' visibility.
If an action is added to a toolbar, its update() can be called if there was any user activity or focus transfer. If the action's availability changes in the absence of these events, then call ActivityTracker.getInstance().inc() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ide/ActivityTracker.java) to notify the action subsystem to update all toolbar actions.
An example of enabling a menu action based on whether a project is open is demonstrated in PopupDialogAction.update() (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/java/org/intellij/sdk/action/PopupDialogAction.java) method.
When the user selects an enabled action, be it from a menu or toolbar, the action's AnAction.actionPerformed() method is called. This method contains the code executed to perform the action, and it is here that the real work gets done.
By using the AnActionEvent methods and CommonDataKeys, objects such as the Project, Editor, PsiFile, and other information is available. For example, the actionPerformed() method can modify, remove, or add PSI elements to a file open in the editor.
The code that executes in the AnAction.actionPerformed() method should execute efficiently, but it does not have to meet the same stringent requirements as the update() method.
An example of inspecting PSI elements is demonstrated in the SDK code sample action_basics PopupDialogAction.actionPerformed() (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/java/org/intellij/sdk/action/PopupDialogAction.java) method.
Every action and action group has a unique identifier. Basing the identifier for a custom action on the fully qualified name of the implementation is the best practice, assuming the package incorporates the <id> ("id" in "Plugin Configuration File") of the plugin. Including the plugin identifier in the action identifier should prevent it from clashing with other plugins' actions. An action must have a unique identifier for each place. It is used in the IDE UI, even though the FQN of the implementation is the same. Definitions of identifiers for the standard IntelliJ Platform actions are in IdeActions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/actionSystem/IdeActions.java).
Groups organize actions into logical UI structures, which in turn can contain other groups. A group of actions can form a toolbar or a menu. Subgroups of a group can form submenus of a menu.
Actions can be included in multiple groups, and thus appear in different places within the UI. An action must have a unique identifier for each place it appears in the UI. See the Action Declaration Reference section for information about how to specify locations.
A new Presentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java) gets created for every place where the action appears. Therefore, the same action can have a different text or icon when it appears in different places of the user interface. Different presentations for the action are created by copying the Presentation returned by the AnAction.getTemplatePresentation() method.
A group's compact attribute specifies whether an action within that group is visible when disabled. See Registering Actions in plugin.xml for an explanation of how the compact attribute is set for a group. If the compact attribute is true for a menu group, an action in the menu only appears if its state is both enabled and visible. In contrast, if the compact attribute is false, an action in the menu appears if its state is disabled but visible. Some menus like Tools have the compact attribute set, so there isn't a way to show an action on the Tools menu if it is not enabled.
Host Menu compact Setting | Action Enabled | Visibility Enabled | Menu Item Visible? | Menu Item Appears Gray? |
|---|---|---|---|---|
T | F | T | F | N/A |
T | T | T | T | F |
F | F | T | T | T |
F | T | T | T | F |
All other combinations of compact, visibility, and enablement produce N/A for gray appearance because the menu item isn't visible.
See the Grouping Actions (Grouping Actions) tutorial for examples of creating action groups.
There are two main ways to register an action: either by listing it in the <actions> ("actions" in "Plugin Configuration File") section of a plugin's plugin.xml (Plugin Configuration File) file or through code.
Registering actions in plugin.xml is demonstrated in the following reference examples, which document all elements and attributes used in the <actions> ("actions" in "Plugin Configuration File") section and describe each element's meaning.
Beginning in 2020.1, an alternate version of an action's menu text can be declared for use depending on where an action appears. Using the <override-text> ("override-text" in "Plugin Configuration File") element, the menu text for an action can be different depending on context: menu location, toolbar, etc. This is also available for groups in 2020.3 and later.
In the <action> element reference example (below) with id attribute VssIntegration.GarbageCollection, the default is to use the menu text "Garbage Collector: Collect _Garbage." The <add-to-group> element declares the action is added to the Tools menu.
However, the <override-text> element declares that text for VssIntegration.GarbageCollection displayed anywhere in the main menu system should be the alternate text "Collect _Garbage." The Tools menu is part of the main menu, so the displayed menu text is "Collect _Garbage." A different context, such as searching for the action using Help | Find Action, displays the default text "Garbage Collector: Collect _Garbage" to give the user additional information about the action.
A second <override-text> element uses place and use-text-of-place attributes to declare the same version of the text used in the main menu is also used in the editor popup menu. Additional <override-text> elements could be used to specify other places where the main menu text should be used.
An example of using <override-text> is demonstrated in the Creating Actions ("Using override-text for an Action" in "Creating Actions") tutorial.
2020.3 Users can locate actions via their name by invoking Help | Find Action.
To allow using alternative names in search, add one or more <synonym> ("synonym" in "Plugin Configuration File") elements inside <action> ("action" in "Plugin Configuration File") or <reference> ("reference" in "Plugin Configuration File"):
<action id="MyAction" text="My Action Name" class="...">
<synonym text="Another Search Term"/>
</action>To provide a localized synonym, specify key instead of text attribute.
2020.3 To exclude a group from appearing in Help | Find Action results (e.g., New... popup), specify searchable="false".
Hard-coding the presentation in the AnAction constructor is discouraged, use inspection Plugin DevKit | Code | Eager creation of action presentation (2023.3) to highlight such problems.
Action and group localization use resource bundles containing property files named $NAME$Bundle.properties, each file consisting of key=value pairs. The action_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics) plugin demonstrates using a resource bundle to localize the group and action entries added to the Editor Popup Menu.
When localizing actions and groups, the text and description attributes are not declared in plugin.xml. Instead, those attribute values vary depending on the locale and get declared in a resource bundle.
The name and location of the resource bundle must be declared in the plugin.xml file. In the case of action_basics, only a default localization resource bundle (/resources/messages/BasicActionsBundle.properties) is provided:
<resource-bundle>messages.BasicActionsBundle</resource-bundle>2020.1 If necessary, a dedicated resource bundle to use for actions and groups can be defined on <actions> ("actions" in "Plugin Configuration File"):
<actions resource-bundle="messages.MyActionsBundle">
<!-- action/group defined here will use keys from MyActionsBundle.properties -->
</actions>See Extending DefaultActionGroup ("Extending DefaultActionGroup" in "Grouping Actions") for a tutorial of localizing Actions and Groups.
For Actions, the key in property files incorporates the action id in this specific structure:
action.<action-id>.text=Translated Action Text
action.<action-id>.description=Translated Action Description
2020.1 If <override-text> is used for an action id, the key includes the place attribute:
action.<action-id>.<place>.text=Place-dependent Translated Action Text
For Groups, the key in the property files incorporates the group id in this specific structure:
group.<group-id>.text=Translated Group Text
group.<group-id>.description=Translated Group Description
2020.3 If <override-text> is used for a group id, the key includes the <place> attribute:
group.<group-id>.<place>.text=Place-dependent Translated Group Text
The places where actions can appear are defined by constants in ActionPlaces (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/actionSystem/ActionPlaces.java). Group IDs for the IntelliJ Platform are defined in PlatformActions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/idea/PlatformActions.xml).
This, and additional information can also be found by using the Code Completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#invoke-basic-completion), Quick Definition (https://www.jetbrains.com/help/idea/viewing-reference-information.html#view-definition-symbols) and Quick Documentation (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation) features.
To lookup existing Action ID (e.g. for use in relative-to-action), UI Inspector (Internal Actions - UI Inspector) can be used.
See the <actions> ("actions" in "Plugin Configuration File") element and its children documentation for details.
<actions>
<action
id="VssIntegration.GarbageCollection"
class="com.example.impl.CollectGarbage"
text="Garbage Collector: Collect _Garbage"
description="Run garbage collector"
icon="icons/garbage.png">
<!--
The second <override-text> element uses the alternate attribute
"use-text-of-place" to define a location (EditorPopup) to use the
same text as is used in MainMenu. It is a way to specify the use
of an alternate menu text in multiple discrete menu groups.
-->
<override-text place="MainMenu" text="Collect _Garbage"/>
<override-text place="EditorPopup" use-text-of-place="MainMenu"/>
<!-- Provide alternative names for searching action by name -->
<synonym text="GC"/>
<add-to-group
group-id="ToolsMenu"
relative-to-action="GenerateJavadoc"
anchor="after"/>
<!-- Add the first and second keystrokes to all keymaps... -->
<keyboard-shortcut
keymap="$default"
first-keystroke="control alt G"
second-keystroke="C"/>
<!-- ...except the "Mac OS X" keymap and its children. -->
<keyboard-shortcut
keymap="Mac OS X"
first-keystroke="control alt G"
second-keystroke="C"
remove="true"/>
<!-- The "Mac OS X 10.5+" keymap and its children will have only
this keyboard shortcut for this action. -->
<keyboard-shortcut
keymap="Mac OS X 10.5+"
first-keystroke="control alt G"
second-keystroke="C"
replace-all="true"/>
<mouse-shortcut
keymap="$default"
keystroke="control button3 doubleClick"/>
</action>
<!--
This action declares neither a text nor a description attribute.
If it has a resource bundle declared, the text and descriptions
will be retrieved based on the action-id incorporated in the key
for a translated string.
-->
<action
id="sdk.action.PopupDialogAction"
class="sdk.action.PopupDialogAction"
icon="SdkIcons.Sdk_default_icon"/>
<group
class="com.example.impl.MyActionGroup"
id="TestActionGroup"
text="Test Group"
description="Group with test actions"
icon="icons/testGroup.png"
popup="true"
compact="true">
<action
id="VssIntegration.TestAction"
class="com.example.impl.TestAction"
text="My Test Action"
description="My test action"/>
<!-- The <separator> element defines a separator between actions.
It can also have an <add-to-group> child element. -->
<separator/>
<group id="TestActionSubGroup"/>
<!-- The <reference> element allows adding an existing action to
the group. The mandatory "ref" attribute specifies the ID of
the action to add. -->
<reference ref="EditorCopy"/>
<add-to-group
group-id="MainMenu"
relative-to-action="HelpMenu"
anchor="before"/>
</group>
</actions>Two steps are required to register an action from code:
First, an instance of the class derived from AnAction must be passed to the registerAction() method of ActionManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionManager.java), to associate the action with an ID.
Second, the action needs to be added to one or more groups. To get an instance of an action group by ID, it is necessary to call ActionManager.getAction() and cast the returned value to DefaultActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java).
If a plugin needs to include a toolbar or popup menu built from a group of actions in its user interface, that is accomplished through ActionPopupMenu (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionPopupMenu.java) and ActionToolbar (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionToolbar.java). These objects can be created through calls to the ActionManager.createActionPopupMenu() and createActionToolbar() methods. To get a Swing component from such an object, call the respective getComponent() method.
If an action toolbar is attached to a specific component (for example, a panel in a tool window), call ActionToolbar.setTargetComponent() and pass the related component's instance as a parameter. Setting the target ensures that the toolbar buttons' state depends on the state of the related component, not on the current focus location within the IDE frame.
See Toolbar (https://jetbrains.design/intellij/controls/toolbar/) in IntelliJ Platform UI Guidelines for an overview.
Use ToggleAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleAction.java) or DumbAwareToggleAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/project/DumbAwareToggleAction.java) for actions with "selected"/"pressed" state (e.g., menu item with checkbox, toolbar action button). See also ToggleOptionAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/ToggleOptionAction.java).
Use BackAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/navigation/BackAction.java) and ForwardAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/navigation/ForwardAction.java) to provide navigation trail taken from History (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/navigation/History.java) provided by History.KEY.
For actions registered at runtime (e.g., in a tool window toolbar), add an <action> ("action" in "Plugin Configuration File") entry with EmptyAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/EmptyAction.java) to "reserve" Action ID, so they become visible in Settings | Keymap.
Sometimes, it is required to execute actions programmatically, e.g., executing an action implementing logic we need and the implementation is out of our control. Executing actions can be achieved with ActionUtils.invokeAction() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ActionUtil.kt).
Executing actions programmatically should be avoided whenever possible. If an action executed programmatically is under your control, extract its logic to a service (Services) or utility class and call it directly.
This tutorial leads you through a series of steps which show how to create, register, and customize custom actions and action groups. By registering actions, you can add your own menu items, toolbar buttons and keyboard shortcuts to the IDE user interface.
Creating Actions (Creating Actions)
Grouping Actions (Grouping Actions)
The source code for the action_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics) code sample is used throughout this tutorial.
Plugins can add actions to existing IDE menus and toolbars, as well as add new menus and toolbars. The IntelliJ Platform calls the actions of plugins in response to user interactions with the IDE. However, the actions of a plugin must first be defined and registered with the IntelliJ Platform.
Using the SDK code sample action_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics), this tutorial illustrates the steps to create an action for a plugin.
Custom actions extend the abstract class AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java). Classes that extend it should override AnAction.update(), and must override AnAction.actionPerformed().
The update() method implements the code that enables or disables an action.
The actionPerformed() method implements the code that executes when an action is invoked by the user.
When targeting IntelliJ Platform 2022.3 or later, AnAction.getActionUpdateThread() must be implemented
As an example, PopupDialogAction (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/java/org/intellij/sdk/action/PopupDialogAction.java) overrides AnAction for the action_basics code sample.
public class PopupDialogAction extends AnAction {
@Override
public void update(@NotNull AnActionEvent event) {
// Using the event, evaluate the context,
// and enable or disable the action.
}
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
// Using the event, implement an action.
// For example, create and show a dialog.
}
// Override getActionUpdateThread() when you target 2022.3 or later!
}AnAction classes do not have class fields of any kind. This restriction prevents memory leaks. For more information about why, see "Action Implementation" in "Actions".
At this stage, update() implicitly defaults always to enable this action. The implementation of actionPerformed() does nothing. These methods are fully implemented in below.
Before fleshing out those methods, to complete this minimal implementation, PopupDialogAction must be registered with the IntelliJ Platform.
Actions are registered by declaring them in code or by declaring them in the <actions> ("actions" in "Plugin Configuration File") section of a plugin configuration file (Plugin Configuration File). This section describes using IDE tooling - the New Action form - to add a declaration to the plugin.xml file, and then tuning registration attributes manually. A more comprehensive explanation of action registration is available in the "Registering Actions" in "Actions" section of this guide.
IntelliJ IDEA has an embedded inspection that spots unregistered actions. Verify the inspection is enabled at Settings | Editor | Inspections | Plugin DevKit | Code | Component/Action not registered. Here is an example for this stage of the PopupDialogAction class:

To register PopupDialogAction and set up its basic attributes press AltShiftEnter. Fill out the New Action form to set up the parameters for PopupDialogAction:

The fields of the form are:
Action ID - Every action must have a unique ID. If the action class is used in only one place in the IDE UI, then the class fully qualified name (FQN) is a good default for the ID. Using the action class in multiple places requires mangling the ID, such as adding a suffix to the FQN, for each ID.
Class Name - The FQN implementation class for the action. If the same action is used in multiple places in the IDE UI, the implementation FQN can be reused with a different Action ID.
Name - The text to appear in the menu.
Description - Hint text to be displayed.
Add to Group - The action group - menu or toolbar - to which the action is added. Clicking on the list of groups and typing invokes a search, such as "ToolsMenu".
Anchor - Where the menu action should be placed in the Tools menu relative to the other actions in that menu.
In this case, PopupDialogAction would be available in the Tools menu, it would be placed at the top, and would have no shortcuts.
After finishing the New Action form and applying the changes, the <actions> ("actions" in "Plugin Configuration File") section of the plugin's plugins.xml file would contain:
<actions>
<action
id="org.intellij.sdk.action.PopupDialogAction"
class="org.intellij.sdk.action.PopupDialogAction"
text="Popup Dialog Action"
description="SDK action example">
<add-to-group group-id="ToolsMenu" anchor="first"/>
</action>
</actions>The <action> ("action" in "Plugin Configuration File") element declares the Action ID (id), Class Name (class), Name (text), and Description from the New Action form. The <add-to-group> ("add-to-group" in "Plugin Configuration File") element declares where the action will appear and mirrors the names of entries from the form.
This declaration is adequate, but adding more attributes is discussed in the next section.
An action declaration can be added manually to the plugin.xml file. An exhaustive list of declaration elements and attributes is presented in "Registering Actions in plugin.xml" in "Actions". Attributes are added by selecting them from the New Action form, or by editing the registration declaration directly in the plugin.xml file.
The <action> ("action" in "Plugin Configuration File") declaration for PopupDialogAction in the action_basics plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/resources/META-INF/plugin.xml) file. It also contains an attribute for an Icon (Working with Icons) and encloses elements declaring text overrides, keyboard and mouse shortcuts, and to which menu group the action should be added.
The full declaration is:
<action
id="org.intellij.sdk.action.PopupDialogAction"
class="org.intellij.sdk.action.PopupDialogAction"
text="Action Basics Plugin: Popup Dialog Action"
description="SDK action example"
icon="SdkIcons.Sdk_default_icon">
<override-text place="MainMenu" text="Popup Dialog Action"/>
<keyboard-shortcut
keymap="$default"
first-keystroke="control alt A"
second-keystroke="C"/>
<mouse-shortcut
keymap="$default"
keystroke="control button3 doubleClick"/>
<add-to-group group-id="ToolsMenu" anchor="first"/>
</action>By using the override-text element introduced in 2020.1 of the IntelliJ Platform, the action text can be different depending on the context of where the action appears: menu, toolbar, etc. The example above uses this element to ensure the shorter text "Popup Dialog Action" is shown anywhere the action appears in the main menu structure. Otherwise, the default, more explanatory text "Action Basics Plugin: Popup Dialog Action" is shown. For more information, see "Setting the override-text Element" in "Actions".
After performing the steps described above, compile and run ("Executing the Plugin" in "Creating a Plugin Gradle Project") the plugin to see the newly created action available as a Tools menu item, which is within the context of the main menu:

To see the alternate, more verbose text declared by the override-text element, use Help | Find Action... and search for "Pop Dialog Action". The search shows the verbose menu text in a context outside the main menu:

Selecting the action from the menu, keyboard/mouse shortcuts, or Find Action won't do anything at this point because the implementations are empty. However, it confirms the new entry appears at Tools | Pop Dialog Action and Help | Find Action....
At this point, the new action PopupDialogAction is registered with the IntelliJ Platform and functions in the sense that update() and actionPerformed() are called in response to user interaction with the IDE Tools menu. However, neither method implements any code to perform useful work.
This section describes adding useful code to these methods. The update() method defaults to always enable the action, which is satisfactory for intermediate testing. So actionPerformed() will be developed first.
Adding code to the PopupDialogAction.actionPerformed() method makes the action do something useful. The code below gets information from the anActionEvent input parameter and constructs a message dialog. A generic icon, and the message and title attributes from the invoking menu action are displayed. However, code in this method could manipulate a project, invoke an inspection, change the contents of a file, etc.
For demonstration purposes the AnActionEvent.getData() method tests if a Navigatable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/pom/Navigatable.java) object is available. If so, information about the selected element is added to the dialog.
See "Determining the Action Context" in "Actions" for more information about accessing information from the AnActionEvent input parameter.
Adding code to PopupDialogAction.update() gives finer control of the action's visibility and availability. The action's state and(or) presentation can be dynamically changed depending on the context.
This method needs to execute very quickly. For more information about this constraint, see the warning in "Overriding the AnAction.update() Method" in "Actions".
In this example, the update() method relies on a Project object being available. This requirement means the user must have at least one project open in the IDE for the PopupDialogAction to be available. So the update() method disables the action for contexts where a Project object isn't defined.
The availability (enabled and visible) is set on the Presentation object. Setting both the enabled state and visibility produces consistent behavior despite possible host menu settings, as discussed in "Grouping Actions" in "Actions".
The update() method does not check to see if a Navigatable object is available before enabling PopupDialogAction. This check is unnecessary because using the Navigatable object is opportunistic in actionPerformed(). See Determining the Action Context ("Determining the Action Context" in "Actions") for more information about accessing information from the AnActionEvent input parameter.
A constructor is overridden in PopupDialogAction, but this is an artifact of reusing this class for a dynamically created menu action. Otherwise, overriding constructors for AnAction is not required.
After compiling and running the plugin project and invoking the action, the dialog will pop up:

If an implementation requires several actions, or there are simply too many actions that overload the menu, the actions can be placed into groups. This tutorial demonstrates adding an action to an existing group, creating a new action group, and action groups with a variable number of actions. The sample code discussed in this tutorial is from the code sample action_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics).
Some content in this tutorial assumes the reader is familiar with the tutorial for Creating Actions (Creating Actions).
In this first example, the action group will be available as a top-level menu item, and actions are represented as drop-down menu items. The group is based on a default IntelliJ Platform implementation.
Grouping can be registered by adding a <group> ("group" in "Plugin Configuration File") element to the <actions> ("actions" in "Plugin Configuration File") section of a plugin's plugin.xml (Plugin Configuration File) file. This example has no class attribute in the <group> element because the IntelliJ Platform framework will supply a default implementation class for the group. This default implementation is used if a set of actions belonging to the group is static, i.e., does not change at runtime, which is the majority of cases. The id attribute must be unique, so incorporating the plugin ID or package name is the best practice.
The popup attribute determines whether actions in the group are placed in a submenu. The icon attribute specifies the FQN of an Icon (Working with Icons) object to be displayed. No compact attribute is specified, which means this group will support submenus. See "Registering Actions in plugin.xml" in "Actions" for more information about these attributes.
<group
id="org.intellij.sdk.action.GroupedActions"
text="Static Grouped Actions"
popup="true"
icon="SdkIcons.Sdk_default_icon"/>The following sample shows how to use an <add-to-group> ("add-to-group" in "Plugin Configuration File") element to place a custom action group relative to an entry in the Tools menu. The attribute relative-to-action references the action id for PopupDialogAction, not a native IntelliJ menu entry. Rather PopupDialogAction is defined in the same plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/resources/META-INF/plugin.xml) file. This group is placed after the single entry for the action PopupDialogAction, as defined in the tutorial Creating Actions ("Registering an Action with the New Action Form" in "Creating Actions").
<group
id="org.intellij.sdk.action.GroupedActions"
text="Static Grouped Actions"
popup="true"
icon="SdkIcons.Sdk_default_icon">
<add-to-group
group-id="ToolsMenu"
anchor="after"
relative-to-action="org.intellij.sdk.action.PopupDialogAction"/>
</group>The PopupDialogAction implementation will be reused and registered in the newly created static group. The id attribute for the reused PopupDialogAction implementation is set to a unique value, org.intellij.sdk.action.GroupPopDialogAction. This value differentiates this new <action> ("action" in "Plugin Configuration File") entry from the id previously used to register this action implementation in the Creating Actions ("Registering an Action with the New Action Form" in "Creating Actions") tutorial. A unique id supports reuse of action classes in more than one menu or group. The action in this group will be displayed in the menu as "A Group Action".
<group
id="org.intellij.sdk.action.GroupedActions"
text="Static Grouped Actions"
popup="true"
icon="SdkIcons.Sdk_default_icon">
<add-to-group
group-id="ToolsMenu"
anchor="after"
relative-to-action="org.intellij.sdk.action.PopupDialogAction"/>
<action
class="org.intellij.sdk.action.PopupDialogAction"
id="org.intellij.sdk.action.GroupPopDialogAction"
text="A Group Action"
description="SDK static grouped action example"
icon="SdkIcons.Sdk_default_icon">
</action>
</group>After performing the steps described above, the action group and its content will be available in the Tools menu. The underlying PopupDialogAction implementation is reused for two entries in the Tools menu:
Once for the top menu entry Tools | Pop Dialog Action with the action id equal to org.intellij.sdk.action.PopupDialogAction as set in the Creating Actions ("Registering an Action with the New Action Form" in "Creating Actions") tutorial.
A second time for the menu entry Tools | Static Grouped Actions | A Group Action with the action id equal to org.intellij.sdk.action.GroupPopDialogAction.

In some cases, the specific behavior of an action group needs to depend on the context. The solution is analogous to making a single action entry dependent on context ("Extending the update() Method" in "Creating Actions").
The steps below show how to make a group of actions available and visible if certain conditions are met. In this case, the condition is having an instance of available editor. This condition is needed because the custom action group is added to an IntelliJ menu that is only enabled for editing.
The DefaultActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) is an implementation of ActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionGroup.java). The DefaultActionGroup class is used to add child actions and separators between them to a group. This class is used if a set of actions belonging to the group does not change at runtime.
As an example, extend DefaultActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/actionSystem/DefaultActionGroup.java) to create the CustomDefaultActionGroup class in the action_basics code sample:
public class CustomDefaultActionGroup extends DefaultActionGroup {
@Override
public void update(AnActionEvent event) {
// Enable/disable depending on whether a user is editing...
}
}As in the case with the static action group, the action <group> ("group" in "Plugin Configuration File") should be declared in the <actions> ("actions" in "Plugin Configuration File") section of the plugin.xml file, for example, the action_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/resources/META-INF/plugin.xml) plugin. For demonstration purposes, this implementation will use localization.
The <group> element declaration below shows:
An optional resource bundle declaration outside the <actions> section for localizing actions.
The presence of the class attribute in the <group> element tells the IntelliJ Platform framework to use CustomDefaultActionGroup rather than the default implementation.
Setting the group's popup attribute to allow submenus.
The text and description attributes are omitted in the <group> declaration in favor of using the localization resource bundle to define them.
There is no icon attribute for the group; the CustomDefaultActionGroup implementation will add an icon for the group.
The <add-to-group> ("add-to-group" in "Plugin Configuration File") element specifies adding the group in the first position of the existing EditorPopupMenu.
<resource-bundle>messages.BasicActionsBundle</resource-bundle>
<actions>
<group
id="org.intellij.sdk.action.CustomDefaultActionGroup"
class="org.intellij.sdk.action.CustomDefaultActionGroup"
popup="true">
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
</group>
</actions>As in Static Grouped Actions, the PopupDialogAction action is added as an <action> ("action" in "Plugin Configuration File") element in the <group> ("group" in "Plugin Configuration File") element. In the <action> element declaration below:
The class attribute in the <action> element has the same FQN to reuse this action implementation.
The id attribute is unique to distinguish it from other uses of the implementation in the Action System.
The text and description attributes are omitted in the <action> declaration; they are instead defined using the localization resource bundle.
The SDK icon is declared for use with this action.
<group
id="org.intellij.sdk.action.CustomDefaultActionGroup"
class="org.intellij.sdk.action.CustomDefaultActionGroup"
popup="true"
icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
<action
id="org.intellij.sdk.action.CustomGroupedAction"
class="org.intellij.sdk.action.PopupDialogAction"
icon="SdkIcons.Sdk_default_icon"/>
</group>Now the translations for the text and description attributes must be provided in the resource bundle BasicActionsBundle.properties (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/resources/messages/BasicActionsBundle.properties) file according to Localizing Actions and Groups ("Localizing Actions and Groups" in "Actions"). Note there are two sets of text and description translations, one for the action and one for the group. Conceivably, there could be another set of translations for the action if it used the <override-text> ("override-text" in "Plugin Configuration File") attribute.
action.org.intellij.sdk.action.CustomGroupedAction.text=A Popup Action [EN]
action.org.intellij.sdk.action.CustomGroupedAction.description=SDK popup grouped action example [EN]
group.org.intellij.sdk.action.CustomDefaultActionGroup.text=Popup Grouped Actions [EN]
group.org.intellij.sdk.action.CustomDefaultActionGroup.description=Custom defaultActionGroup demo [EN]Override the CustomDefaultActionGroup.update() method to make the group visible only if there's an instance of the editor available. Also, a custom icon is added to demonstrate that group icons can be changed depending on the action context:
public class CustomDefaultActionGroup extends DefaultActionGroup {
@Override
public void update(AnActionEvent event) {
// Enable/disable depending on whether a user is editing
Editor editor = event.getData(CommonDataKeys.EDITOR);
event.getPresentation().setEnabled(editor != null);
// Take this opportunity to set an icon for the group.
event.getPresentation().setIcon(SdkIcons.Sdk_default_icon);
}
}After compiling and running the code sample above and opening a file in the editor and right-clicking, the Editor context menu will pop up containing a new group of actions in the first position. Note that the group and actions come from the resource file as all contain the suffix " [EN]". The new group will also have an icon:

If a set of actions belonging to a custom group varies depending on the context, the group must extend ActionGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ActionGroup.java). The set of actions in the ActionGroup is dynamically defined.
To create a group of actions with a variable number of actions, extend ActionGroup. For example, as in the action_basics class DynamicActionGroup (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/java/org/intellij/sdk/action/DynamicActionGroup.java) code:
public class DynamicActionGroup extends ActionGroup {
}To register the dynamic menu group, a <group> ("group" in "Plugin Configuration File") attribute needs to be placed in the <actions> ("actions" in "Plugin Configuration File") section of plugin .xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/action_basics/src/main/resources/META-INF/plugin.xml). When enabled, this group appears just below the Static Grouped Actions in the Tools menu:
<group
id="org.intellij.sdk.action.DynamicActionGroup"
class="org.intellij.sdk.action.DynamicActionGroup"
popup="true"
text="Dynamically Grouped Actions"
description="SDK dynamically grouped action example"
icon="SdkIcons.Sdk_default_icon">
<add-to-group
group-id="ToolsMenu"
anchor="after"
relative-to-action="org.intellij.sdk.action.GroupedActions"/>
</group>If a<group> element's class attribute names a class derived from ActionGroup, then any static <action> declarations in that group throw an exception. For a statically defined group, use DefaultActionGroup.
To add actions to the DynamicActionGroup, a non-empty array of AnAction instances should be returned from the DynamicActionGroup.getChildren() method. Here again, reuse the PopupDialogAction implementation. This use case is why PopupDialogAction overrides a constructor:
public class DynamicActionGroup extends ActionGroup {
@NotNull
@Override
public AnAction[] getChildren(AnActionEvent event) {
return new AnAction[]{
new PopupDialogAction(
"Action Added at Runtime",
"Dynamic Action Demo",
SdkIcons.Sdk_default_icon)
};
}
}After providing the implementation of DynamicActionGroup and making it return a non-empty array of actions, the third position in the Tools menu will contain a new group of actions:

The IntelliJ Platform Persistence Model is used to store a variety of information. For example, Run Configurations (Run Configurations) and Settings (Settings) are stored using the Persistence Model.
There are two distinct approaches, depending on the type of data being persisted:
Persisting State of Components (Persisting State of Components)
Persisting Sensitive Data (Persisting Sensitive Data)
The IntelliJ Platform provides an API that allows components or services to persist their state between restarts of the IDE. The API allows for persisting simple key-value entries and complex state classes.
For persisting sensitive data like passwords, see Persisting Sensitive Data (Persisting Sensitive Data).
The PersistentStateComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) interface allows for persisting state classes and gives the most flexibility for defining the values to be persisted, their format, and storage location.
To use it:
mark a service (Services) (project or application-level service for storing project or application data, respectively) as implementing the PersistentStateComponent interface
define the state class
specify the storage location using @State (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/State.java)
Note that instances of extensions cannot persist their state by implementing PersistentStateComponent. If an extension needs to have a persistent state, define a separate service responsible for managing that state.
The easiest way to implement a persistent state component in Kotlin is extending SimplePersistentStateComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/SimplePersistentStateComponent.kt), which implements PersistentStateComponent.
SimplePersistentStateComponent is parameterized by a subclass of BaseState (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/BaseState.kt). BaseState provides a set of handy property delegates (https://kotlinlang.org/docs/delegated-properties.html), which make it easy to create properties with default values. In addition, delegates track property modifications internally, which helps decrease calling PersistentStateComponent.getState() by the platform.
It is recommended to create separate classes for a component and its state:
@Service
@State(...)
class MySettings : SimplePersistentStateComponent<MyState>(MyState())
class MyState : BaseState() {
var value by string()
}The implementation of PersistentStateComponent must be parameterized with the type of state class. The state class can either be a separate class, or the class implementing PersistentStateComponent.
In this case, the state class instance is typically stored as a field in the PersistentStateComponent class. When the state is loaded from the storage, it is assigned to the state field (see loadState()):
@Service
@State(...)
class MySettings implements PersistentStateComponent<MySettings.State> {
static class State {
public String value;
}
private State myState = new State();
public State getState() {
return myState;
}
public void loadState(State state) {
myState = state;
}
}Using a separate state class is the recommended approach.
In this case, getState() returns the component itself, and loadState() copies properties of the state loaded from storage to the component instance:
@Service
@State(...)
class MySettings implements PersistentStateComponent<MySettings> {
public String stateValue;
public MySettings getState() {
return this;
}
public void loadState(MySettings state) {
XmlSerializerUtil.copyBean(state, this);
}
}The implementation of PersistentStateComponent works by serializing public fields, annotated (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations) private fields (see also Customizing the XML format of persisted values), and bean properties into an XML format.
To exclude a public field or bean property from serialization, annotate the field or getter with @Transient (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Transient.java).
Note that the state class must have a default constructor. It should return the component's default state: the one used if there is nothing persisted in the XML files yet.
State class should have an equals() method, but state objects are compared by fields if it is not implemented.
The following types of values can be persisted:
numbers (both primitive types, such as int, and boxed types, such as Integer)
booleans
strings
collections
maps
enums
For other types, extend Converter (https://github.com/JetBrains/intellij-community/tree/master/platform/util/src/com/intellij/util/xmlb/api.kt). See the example below.
class LocalDateTimeConverter extends Converter<LocalDateTime> {
public LocalDateTime fromString(@NotNull String value) {
long epochMilli = Long.parseLong(value);
ZoneId zoneId = ZoneId.systemDefault();
return Instant.ofEpochMilli(epochMilli).atZone(zoneId).toLocalDateTime();
}
public String toString(LocalDateTime value) {
ZoneId zoneId = ZoneId.systemDefault();
long toEpochMilli = value.atZone(zoneId).toInstant().toEpochMilli();
return Long.toString(toEpochMilli);
}
}Define the converter above in @OptionTag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/OptionTag.java) or @Attribute (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Attribute.java):
class State {
@OptionTag(converter = LocalDateTimeConverter.class)
public LocalDateTime dateTime;
}To specify where precisely the persisted values are stored, add @State annotation to the PersistentStateComponent class.
It has the following fields:
name (required) — specifies the name of the state (name of the root tag in XML).
storages — one or more of @Storage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java) annotations to specify the storage locations. Optional for project-level values — standard project file is used in this case.
reloadable (optional) — if set to false, a full project (or application) reload is required when the XML file is changed externally, and the state has changed.
The simplest ways of specifying the @Storage annotation are as follows:
@Storage(StoragePathMacros.WORKSPACE_FILE) - for values stored in the project workspace file (project-level components only).
@Storage("yourName.xml") - if a component is project-level, for .ipr based projects, standard project file is used automatically, and there is no need to specify anything.
The state is persisted in a separate file by specifying a different setting for the value parameter, which was the file parameter before 2016.x.
For application-level storage, it is strongly recommended to use a custom file. Using of other.xml is deprecated.
When planning your storage location, consider its intended purpose. A project-level custom file should be preferred for storing plugin settings. To store cached values, use @Storage(StoragePathMacros.CACHE_FILE). Refer to StoragePathMacros (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java) for commonly used macros.
The roamingType parameter of the @Storage annotation specifies the roaming type when the settings are shared:
RoamingType.DEFAULT - settings are shared
RoamingType.PER_OS - settings are shared per operating system
RoamingType.DISABLED - settings sharing is disabled
If there are multiple components that store state in the same file, they must have the same roamingType attribute value.
It is possible to share the persistent state of components between different IDE installations. This allows users to have the same settings on every development machine or to share their settings within a team.
Settings can be shared via the following functionalities:
Settings Sync (https://www.jetbrains.com/help/idea/sharing-your-ide-settings.html#IDE_settings_sync) plugin that allows synchronizing settings on JetBrains servers. Users can choose the category of settings that are synchronized.
Settings Repository (https://www.jetbrains.com/help/idea/sharing-your-ide-settings.html#settings-repository) plugin that allows synchronizing settings in a Git repository created and configured by a user.
Export Settings (https://www.jetbrains.com/help/idea/2019.3/sharing-your-ide-settings.html#import-export-settings) feature that allows for the manual import and export of settings.
Synchronization via the Settings Sync or Settings Repository plugins only works when these plugins are installed and enabled.
The decision about making a specific component's state shareable should be made carefully. Only the settings that are not specific to a given machine should be shared, e.g., paths to user-specific directories shouldn't be shared. If a component contains both shareable and non-shareable data, it should be split into two separate components.
The Settings Sync plugin is available starting with version 2022.3.
To include a plugin's component state in the Settings Sync plugin synchronization, the following requirements must be met:
The RoamingType is defined via the roamingType attribute of the @Storage annotation and is not equal to DISABLED.
The SettingsCategory is defined via the category attribute of the @State annotation and is not equal to OTHER.
There is no other PersistentStateComponent, which is stored in the same XML file and has a different RoamingType.
If the component state is OS-dependent, the roamingType of the @Storage annotation must be set to RoamingType.PER_OS.
Note that other.xml file is non-roamable and declaring it in the @Storage annotation will disable roaming of the component state. It is recommended to use a separate XML file for the component or use another existing storage file.
The Settings Repository plugin is unbundled starting with version 2022.3 and will be no longer maintained.
Persistent components can be shared via the Settings Repository plugin and Export Settings feature, depending on the roamingType of the @Storage annotation. See the for more details.
Consider using annotation parameters only to achieve backward compatibility. Otherwise, feel free to file issues about specific serialization cosmetics.
If you want to use the default bean serialization but need to customize the storage format in XML (for example, for compatibility with previous versions of a plugin or externally defined XML formats), use the @Tag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Tag.java), @Attribute (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Attribute.java), @Property (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/Property.java), @MapAnnotation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/MapAnnotation.java), @XMap (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/XMap.java), and @XCollection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/xmlb/annotations/XCollection.java) annotations.
If the state to serialize doesn't map cleanly to a JavaBean, then org.jdom.Element can be used as the state class. In that case, use the getState() method to build an XML element with an arbitrary structure, which then is saved directly in the state XML file. In the loadState() method, deserialize the JDOM element tree using any custom logic. This is not recommended and should be avoided whenever possible.
If the underlying persistence model or storage format has changed, a ConverterProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/conversion/ConverterProvider.java) can provide ProjectConverter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/conversion/ProjectConverter.java), whose getAdditionalAffectedFiles() method returns affected files to migrate and performs programmatic migration of stored values.
The PersistentStateComponent.loadState() method is called after the component has been created (only if there is some non-default state persisted for the component), and after the XML file with the persisted state is changed externally (for example, if the project file was updated from the version control system). In the latter case, the component is responsible for updating the UI and other related components according to the changed state.
The PersistentStateComponent.getState() method is called every time the settings are saved (for example, on frame deactivation or when closing the IDE). If the state returned from getState() is equal to the default state (obtained by creating the state class with a default constructor), nothing is persisted in the XML. Otherwise, the returned state is serialized in XML and stored.
If the plugin needs to persist just a few simple values, the easiest way to do so is to use the PropertiesComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/util/PropertiesComponent.java) service. It can save both application-level values and project-level values in the workspace file. Roaming is disabled for PropertiesComponent, so use it only for temporary, non-roamable properties.
Use the PropertiesComponent.getInstance() method for storing application-level values, and the PropertiesComponent.getInstance(Project) method for storing project-level values.
Since all plugins share the same namespace, it is highly recommended prefixing key names (e.g., using plugin ID com.example.myCustomSetting).
Older components use the JDOMExternalizable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/JDOMExternalizable.java) interface for persisting state. It uses the readExternal() method for reading the state from a JDOM element, and writeExternal() to write the state.
Implementations can manually store the state in attributes and sub-elements or use the DefaultJDOMExternalizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/DefaultJDOMExternalizer.java) class to store the values of all public fields automatically.
Components save their state in the following files:
Project-level: project (.ipr) file. However, if the workspace option in the plugin.xml (Plugin Configuration File) file is set to true, then the workspace (.iws) file is used instead.
Module-level: module (.iml) file.
The Credentials Store API allows you to store sensitive user data securely, like passwords, server URLs, etc.
Use PasswordSafe (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/remote-core/src/ide/passwordSafe/PasswordSafe.kt) to work with credentials.
Common Utility Method:
private CredentialAttributes createCredentialAttributes(String key) {
return new CredentialAttributes(
CredentialAttributesKt.generateServiceName("MySystem", key)
);
}String key = null; // e.g. serverURL, accountID
CredentialAttributes attributes = createCredentialAttributes(key);
PasswordSafe passwordSafe = PasswordSafe.getInstance();
Credentials credentials = passwordSafe.get(attributes);
if (credentials != null) {
String password = credentials.getPasswordAsString();
}
// or get password only
String password = passwordSafe.getPassword(attributes);CredentialAttributes attributes = createCredentialAttributes(key);
Credentials credentials = new Credentials(username, password);
PasswordSafe.getInstance().set(attributes, credentials);To remove stored credentials, pass null for the credentials parameter.
The default storage format depends on the OS.
OS | Storage |
|---|---|
Windows | File in KeePass (https://keepass.info) format |
macOS | Keychain using Security Framework (https://developer.apple.com/documentation/security/keychain_services) |
Linux | Secret Service API (https://specifications.freedesktop.org/secret-service/latest/) using libsecret (https://wiki.gnome.org/Projects/Libsecret) |
Users can override the default behavior in Settings | Appearance & Behavior | System Settings | Passwords.
Product Help: Settings (https://www.jetbrains.com/help/idea/settings-preferences-dialog.html), Configuring the IDE (https://www.jetbrains.com/help/idea/configuring-project-and-ide-settings.html)
Settings are but one application of the IntelliJ Platform Persistence Model (Persistence Model). For more information, see:
Settings Guide (Settings Guide) for information about Settings Extension Points and implementations.
Custom Settings Groups (Custom Settings Groups) for information about creating custom Settings groups and parent-child relationships.
Settings Tutorial (Settings Tutorial) for step-by-step instructions for creating a simple set of custom Settings.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Settings persistently store states that control the behavior and appearance of IntelliJ Platform-based IDEs. On this page, the term "Settings" means the same as "Preferences" on some platforms.
Plugins can create and store Settings to capture their configuration in a way that uses the IntelliJ Platform Persistence Model (Persisting State of Components). The User Interface (UI) for these custom Settings can be added to the IDE Settings dialog (https://www.jetbrains.com/help/idea/settings-preferences-dialog.html).
Settings can affect different levels (https://www.jetbrains.com/help/idea/configuring-project-and-ide-settings.html) of scope. This document describes adding custom Settings at the Project and Application (or Global, IDE) levels.
See Settings Tutorial (Settings Tutorial) for step-by-step instructions for creating a simple set of custom Settings.
See "Inspecting Settings" in "Internal Actions - UI Inspector" on how to gather information in the IDE instance for Settings dialog.
Custom Settings implementations are declared in the plugin.xml (Plugin Configuration File) file using one of two extension points (EP), depending on the level of the Settings. Many attributes are shared between the EP declarations.
Application and Project Settings typically provide an implementation based on the Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) interface because they do not have runtime dependencies. See Implementations for Settings Extension Points for more information.
For performance reasons, it is recommended to declare as much information as possible about a 'Settings' implementation using attributes in the EP element in the plugin.xml descriptor. If it is not declared, the component must be loaded to retrieve it from the implementation, degrading UI responsiveness.
The application-level settings are declared using com.intellij.applicationConfigurable EP.
An example <applicationConfigurable> EP declaration is shown below. The declaration indicates the settings are a child of the tools settings group, the implementation FQN is com.example.ApplicationSettingsConfigurable, the unique ID is the same as the implementation fully qualified name (FQN), and the (non-localized) title displayed to users is "My Application Settings". See for more information.
<extensions defaultExtensionNs="com.intellij">
<applicationConfigurable
parentId="tools"
instance="com.example.ApplicationSettingsConfigurable"
id="com.example.ApplicationSettingsConfigurable"
displayName="My Application Settings"/>
</extensions>The project-level settings are declared using com.intellij.projectConfigurable EP.
An example <projectConfigurable> EP declaration is shown below. Similar to the application setting example above, but it includes the additional attribute nonDefaultProject indicating these settings do not apply to the default project (https://www.jetbrains.com/help/idea/configure-project-settings.html#new-default-settings). See for details.
<extensions defaultExtensionNs="com.intellij">
<projectConfigurable
parentId="tools"
instance="com.example.ProjectSettingsConfigurable"
id="com.example.ProjectSettingsConfigurable"
displayName="My Project Settings"
nonDefaultProject="true"/>
</extensions>Readers are encouraged to review the Javadoc comments for Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) because the attribute information applies to ConfigurableProvider as well as Configurable, as noted. This section provides some additional clarification of those comments.
The attributes supported by com.intellij.applicationConfigurable EP and com.intellij.projectConfigurable EP are in the table below:
Attribute | Required | Attribute Value | Implementation Basis |
|---|---|---|---|
instance | yes (1) | FQN of implementation. See for more information. | Configurable |
provider | yes (1) | FQN of implementation. See for more information. | ConfigurableProvider |
nonDefaultProject | yes | Applicable only to the com.intellij.projectConfigurable (project Settings) EP. true = show Settings for all projects except the default project (https://www.jetbrains.com/help/idea/configure-project-settings.html#new-default-settings). false = show Settings for all projects. | Configurable |
displayName | yes (2) | The non-localized Settings name visible to users, which is needed for the Settings dialog left-side menu. For a localized visible name omit displayName and use the key and bundle attributes. | Configurable ConfigurableProvider |
key and bundle | yes (2) | The localization ("Message Bundles" in "Internationalization") key and bundle for the Settings name visible to users. For non-localized visible names omit key and bundle and use displayName. | Configurable ConfigurableProvider |
id | yes | The unique, FQN identifier for this implementation. The FQN should be based on the plugin id to ensure uniqueness. | Configurable ConfigurableProvider |
parentId | yes | This attribute is used to create a hierarchy of Settings. This component is declared one of the specified parentId component's children. Typically used for placing a Settings panel within the Settings Dialog menu. Acceptable values for parentId are given in . groupId is deprecated. (3) | Configurable ConfigurableProvider |
groupWeight | no | Specifies the weight (stacking order) of this component within the group of a parent configurable component. The default weight is 0, meaning lowest in the order. If one child in a group or a parent component has non-zero weight, all children will be sorted descending by their weight. If the weights are equal, the components will be sorted ascending by their display name. | Configurable ConfigurableProvider |
dynamic | no | This component's children are dynamically calculated by calling the getConfigurables() method. Not recommended because it requires loading additional classes while building a Settings tree. If possible, use XML attributes instead. | Configurable.Composite |
childrenEPName | no | Specifies the FQN name of the Extension Point that will be used to calculate the children of this component. | Configurable |
(1) Either instance or provider must be specified depending on the implementation.
(2) Either displayName or key and bundle must be specified depending on whether the displayed Settings name is localized.
(3) If both groupId and parentId are specified, a warning is logged. Also, see default entry in .
The table below shows all Settings groups and their corresponding value for the parentId attribute. See the previous section for all supported attributes.
Group | parentId Value | Details | |
|---|---|---|---|
Appearance & Behavior | appearance | This child group contains Settings to personalize IDE appearance, such as: changing themes and font size. Also, it covers Settings to customize behavior such as keymaps, configuring plugins, and system Settings such as password policies, HTTP proxy, updates, and more. | |
Build, Execution, Deployment | build | Child group containing Settings to configure project integration with different build tools, modify the default compiler Settings, manage server access configurations, customize the debugger behavior, etc. | |
Build Integration | build.tools | A subgroup of build. This subgroup configures project integration with build tools such as Maven, Gradle, or Gant. | |
Editor | editor | Child group containing Settings to personalize source code appearance, such as fonts, highlighting styles, indents, etc. It also contains Settings to customize the editor's appearance, such as line numbers, caret placement, tabs, source code inspections, setting up templates, and file encodings. | |
Languages and Frameworks | language | Child group containing Settings related to specific language frameworks and technologies used in the project. | |
3rd Party Settings | tools | Child group containing Settings to configure integration with third-party applications, specify the SSH Terminal connection Settings, manage server certificates and tasks, configure diagrams layout, etc. | |
Super Parent | root | The invisible parent of all existing groups. Not used except for IDEs built on top of the IntelliJ Platform, or extensive suites of Settings. You should not place settings in this group. | |
other Do not use | default | If neither parentId nor groupId attribute is set, the component is added to the other Settings group. This is undesirable; see other group description. | |
Catch-all Deprecated | other | The IntelliJ Platform no longer uses this group. Do not use this group. Use the tools group instead. | |
Project-related Settings Deprecated | project | The IntelliJ Platform no longer uses this group. It was intended to store some project-related settings. Do not use this group. |
Implementations for com.intellij.projectConfigurable EP and com.intellij.applicationConfigurable EP can have one of two bases:
The Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) interface, which provides a named configurable component with a Swing form. Most Settings providers are based on the Configurable interface or one of its sub- or supertypes.
The ConfigurableProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/ConfigurableProvider.java) class, which can hide a configurable component from the Settings dialog based on runtime conditions.
Many Settings in the intellij-community code base implement Configurable or one of its subtypes, such as SearchableConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/SearchableConfigurable.java). Readers are encouraged to review the Javadoc comments for Configurable.
Implementations must meet several requirements for constructors.
Application Settings implementations, declared using the applicationConfigurable EP, must have a default constructor with no arguments.
Project Settings implementations, declared using the projectConfigurable EP, must declare a constructor with a single argument of type Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java).
Beginning in 2020.2, constructor injection (other than for Project) is not allowed.
For a Configurable implementation correctly declared using an EP, the implementation's constructor is not invoked by the IntelliJ Platform until a user chooses the corresponding Settings displayName in the Settings Dialog menu.
The IntelliJ Platform may instantiate a Configurable implementation on a background thread, so creating Swing components in a constructor can degrade UI responsiveness.
The instantiation of a generic Configurable implementation is documented in the interface file. A few high-level points are reviewed here:
The Configurable.reset() method is invoked immediately after Configurable.createComponent(). Initialization of Setting values in the constructor or createComponent() is unnecessary.
See the section for information about when a Settings object is instantiated.
Once instantiated, a Configurable instance's lifetime continues regardless of whether the implementation's Settings are changed, or the user chooses a different entry on the Settings Dialog menu.
A Configurable instance's lifetime ends when OK or Cancel is selected in the Settings Dialog. An instance's Configurable.disposeUIResources() is called when the Settings Dialog is closing.
To open Settings dialog or show specific Configurable, see ShowSettingsUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/options/ShowSettingsUtil.java).
Implementations based on Configurable can implement marker interfaces, which provide additional flexibility in the implementation.
Do not to add scroll bars to the form. By default, a plugin's Settings component is put into a scrollable pane. However, a Settings panel can have a JTree, which requires its own JScrollPane. So NoScroll interface should be used to remove the outer JScrollPane.
Do not add an empty border to the form. By default, an empty border is added for a plugin's Settings component.
(2022.3) Adds Beta label next to settings page title in Settings tree.
There are classes in the IntelliJ Platform specialized in particular types of Settings. These subtypes are based on com.intellij.openapi.options.ConfigurableEP. For example, Settings | Editor | General | Appearance allows adding Settings via EditorSmartKeysConfigurableEP (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/application/options/editor/EditorSmartKeysConfigurableEP.java) registered in com.intellij.editorSmartKeysConfigurable EP.
Existing implementations of Configurable in the IntelliJ Platform that can serve as a reference are:
ConsoleConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/execution/console/ConsoleConfigurable.java) (application configurable)
AutoImportOptionsConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/application/options/editor/AutoImportOptionsConfigurable.kt) (project configurable)
The ConfigurableProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/ConfigurableProvider.java) class only provides a Configurable implementation if its runtime conditions are met. The IntelliJ Platform first calls the ConfigurableProvider.canCreateConfigurable(), which evaluates runtime conditions to determine if Settings changes make sense in the current context. If the Settings make sense to display, canCreateConfigurable() returns true. In that case the IntelliJ Platform calls ConfigurableProvider.createConfigurable(), which returns the Configurable instance for its Settings implementation.
By choosing not to provide a Configuration implementation in some circumstances, the ConfigurableProvider opts out of the Settings display and modification process. The use of ConfigurableProvider as a basis for a Settings implementation is declared using attributes in the EP declaration.
Examples:
RunToolbarSettingsConfigurableProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/runToolbar/RunToolbarSettingsConfigurableProvider.kt)
VcsManagerConfigurableProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurableProvider.java)
As described in "Extension Points for Settings" in "Settings Guide", custom Settings can be declared as children of existing parent groups such as Tools. These parent groups are the existing categories of Settings in the IntelliJ Platform-based IDE.
However, suppose the custom Settings are rich enough to require multiple levels? For example, a custom Setting implementation has multiple sub-Settings implementations. Extension Point declarations can create this kind of multilayer Settings hierarchy.
See "Inspecting Settings" in "Internal Actions - UI Inspector" on how to gather information in the IDE instance for Settings dialog.
There are multiple ways of creating parent-child relationships in groups of Settings: in implementations, or Extension Point declarations. However, there are performance penalties for creating these relationships in implementations because the objects must be instantiated to determine the relationships. This section describes the syntax for declaring more complex parent-child relationships in com.intellij.projectConfigurable or com.intellij.applicationConfigurable EPs.
An application configurable can be a parent of a project configurable.
There are two ways of declaring parent-child relationships using the com.intellij.projectConfigurable EP or com.intellij.applicationConfigurable EP. The first is to use separate EP declarations that are tied together by the value of one attribute. The second method is to use nested declarations.
One way of declaring a parent-child relationship is by using two separate declarations. This form can be used regardless of whether the parent Settings declaration is in the same plugin. If the id attribute of the parent is known, a plugin can add Settings as a child of that parent.
For example, below are two declarations for project Settings. The first gets added to the tools group, and the second gets added to the id of the parent. The id of the second, child <projectConfigurable> adds a suffix (servers) to the id of the parent.
<extensions defaultExtensionNs="com.intellij">
<projectConfigurable
parentId="tools"
id="com.intellij.sdk.tasks"
displayName="Tasks"
nonDefaultProject="true"
instance="com.intellij.sdk.TaskConfigurable"/>
<projectConfigurable
parentId="com.intellij.sdk.tasks"
id="com.intellij.sdk.tasks.servers"
displayName="Servers"
nonDefaultProject="true"
instance="com.intellij.sdk.TaskRepositoriesConfigurable"/>
</extensions>See the Attributes for Parent-Child Settings EPs section for details about the suffix id.
A shorthand for the separate declaration approach is using the configurable property. This approach nests the child's Settings declaration within the com.intellij.projectConfigurable or com.intellij.applicationConfigurable EP.
When using configurable there isn't a parentId for the child because the nesting implies it. As with using separate EP declarations, formatting restrictions are placed on the child's id attribute - the suffix (servers) gets added. See the section.
The example below demonstrates a nested configurable declaration:
<extensions defaultExtensionNs="com.intellij">
<projectConfigurable
parentId="tools"
id="com.intellij.sdk.tasks"
displayName="Tasks"
nonDefaultProject="true"
instance="com.intellij.sdk.TaskConfigurable"/>
<configurable
id="com.intellij.sdk.tasks.servers"
displayName="Servers"
nonDefaultProject="true"
instance="com.intellij.sdk.TaskRepositoriesConfigurable"/>
</projectConfigurable>
</extensions>Within the parent <projectConfigurable> EP declaration above, more <configurable> declarations could be added as sibling Settings.
There is only one unique attribute when declaring a child Settings EP. The other attributes are the same as discussed in "Settings Declaration Attributes" in "Settings Guide".
For the child of a parent, the id attribute becomes compound:
Attribute | Required | Value |
|---|---|---|
id | Y | Compound FQN of implementation based on com.intellij.openapi.options.Configurable in the form: XX.YY where:
|
All children share the parent's id as the basis of their own id. All children have an id suffix that is unique among their siblings.
Implementations can be based on Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java), ConfigurableProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/ConfigurableProvider.java) or one of their subtypes. For more information about creating Settings implementations, see "Implementations for Settings Extension Points" in "Settings Guide".
The Configurable.Composite interface indicates a configurable component has child components. The preferred approach is to specify child components in the EP declaration. Using the Composite interface incurs the penalty of loading child classes while building the tree of Settings Swing components.
As discussed in the Settings Guide, plugins can add Settings to IntelliJ Platform-based IDEs. The IDE displays the Settings in response to a user choosing Settings. Custom Settings are displayed and function just like those native to the IDE.
Using the SDK code sample settings (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings), this tutorial illustrates the steps to create custom Application-level Settings. Many IntelliJ Platform Settings implementations use fewer classes, but the settings code sample factors the functionality into three classes for clarity:
The AppSettingsConfigurable (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java) is analogous to a Controller in the MVC model - it interacts with the other two Settings classes and the IntelliJ Platform,
The AppSettingsState (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings/src/main/java/org/intellij/sdk/settings/AppSettingsState.java) is like a Model because it stores the Settings persistently,
The AppSettingsComponent (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java) is similar to a View because it displays and captures edits to the values of the Settings.
The structure of the implementation is the same for Project Settings, but there are minor differences in the Configurable implementation ("Constructors" in "Settings Guide") and extension point (EP) declaration ("Declaring Project Settings" in "Settings Guide").
See MarkdownSettings (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettings.kt) and MarkdownSettingsConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/src/org/intellij/plugins/markdown/settings/MarkdownSettingsConfigurable.kt) classes for the settings example implemented in Kotlin with usage of Kotlin UI DSL (Kotlin UI DSL Version 2).
The AppSettingsState class persistently stores the custom Settings. It is based on the IntelliJ Platform Persistence Model ("Using PersistentStateComponent" in "Persisting State of Components").
Given a Light Service ("Light Services" in "Services") is not used, the persistent data class must be declared as a Service ("Declaring a Service" in "Services") EP in the plugin.xml (Plugin Configuration File) file. If these were Project Settings, the com.intellij.projectService EP would be used. However, because these are Application Settings, the com.intellij.applicationService EP is used with the fully qualified name (FQN) of the implementation class:
<extensions defaultExtensionNs="com.intellij">
<applicationService
serviceImplementation="org.intellij.sdk.settings.AppSettingsState"/>
</extensions>As discussed in Implementing the PersistentStateComponent Interface ("Implementing the PersistentStateComponent Interface" in "Persisting State of Components"), AppSettingsState uses the pattern of implementing PersistentStateComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) itself:
The @State (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/State.java) annotation, located just above the class declaration, defines the data storage location ("Defining the Storage Location" in "Persisting State of Components"). For AppSettingsState, the data name parameter is the FQN of the class. Using FQN is the best practice to follow, and is required if custom data gets stored in the standard project or workspace files.
The storages parameter utilizes the @Storage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java) annotation to define a custom file name for the AppSettingsState data. In this case, the file is located in the options directory of the configuration directory (https://www.jetbrains.com/help/idea/tuning-the-ide.html#config-directory) for the IDE.
The AppSettingState implementation has two public fields: a String and a boolean. Conceptually, these fields hold the name of a user, and whether that person is an IntelliJ IDEA user, respectively. See Implementing the State Class ("Implementing the State Class" in "Persisting State of Components") for more information about how PersistentStateComponent serializes public fields.
The fields are so limited and straightforward for this class that encapsulation is not used for simplicity. All that's needed for functionality is to override the two methods called by the IntelliJ Platform when a new component state is loaded (PersistentStateComponent.loadState()), and when a state is saved (PersistentStateComponent.getState()). See PersistentStateComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/components/PersistentStateComponent.java) for more information about these methods.
One static convenience method has been added - AppSettingState.getInstance() - which allows AppSettingsConfigurable to easily acquire a reference to AppSettingState.
The role of the AppSettingsComponent (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings/src/main/java/org/intellij/sdk/settings/AppSettingsComponent.java) is to provide a JPanel for the custom Settings to the IDE Settings Dialog. The AppSettingsComponent has-a JPanel, and is responsible for its lifetime. The AppSettingsComponent is instantiated by AppSettingsConfigurable.
The AppSettingsComponent defines a JPanel containing a JBTextField (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBTextField.java) and a JBCheckBox (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/components/JBCheckBox.java) to hold and display the data that maps to the data fields of AppSettingsState:
The constructor builds the JPanel using the convenient FormBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/util/ui/FormBuilder.java), and saves a reference to the JPanel. The rest of the class are simple accessors and mutators to encapsulate the UI components used on the JPanel.
The methods of AppSettingsConfigurable (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/settings/src/main/java/org/intellij/sdk/settings/AppSettingsConfigurable.java) are called by the IntelliJ Platform, and AppSettingsConfigurable in turn interacts with AppSettingsComponent and AppSettingState.
As described in Declaring Application Settings ("Declaring Application Settings" in "Settings Guide"), the com.intellij.applicationConfigurable is used as the EP. An explanation of this declaration can be found in Declaring Application Settings ("Declaring Application Settings" in "Settings Guide"):
<extensions defaultExtensionNs="com.intellij">
<applicationConfigurable
parentId="tools"
instance="org.intellij.sdk.settings.AppSettingsConfigurable"
id="org.intellij.sdk.settings.AppSettingsConfigurable"
displayName="SDK: Application Settings Example"/>
</extensions>The AppSettingsConfigurable class implements Configurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/Configurable.java) interface. The class has one field to hold a reference to the AppSettingsComponent.
All the methods in this class are overrides of the methods in the Configurable interface. Readers are encouraged to review the Javadoc comments for the Configurable methods. Also review notes about IntelliJ Platform Interactions ("IntelliJ Platform Interactions with Configurable" in "Settings Guide") with Configurable methods.
After performing the steps described above, compile and run the plugin in a Development Instance to see the custom Settings available in the Settings Dialog. Open the IDE Settings by selecting Settings | Tools | SDK: Application Settings Example. The settings are preloaded with the default values:

Now edit the settings values to "John Doe" and click the checkbox. Click on the OK button to close the Settings dialog and save the changes. Exit the Development Instance.
Open the file SdkSettingsPlugin.xml to see the Settings persistently stored. In this demonstration the file resides in code_samples/settings/build/idea-sandbox/config/options/, but see IDE Development Instances (IDE Development Instance) for the general Development Instance case, or Default IDE directories (https://www.jetbrains.com/help/idea/tuning-the-ide.html#default-dirs) if you are testing the settings plugin directly in an IDE.

The Virtual File System (VFS) is a component of the IntelliJ Platform that encapsulates most of its activity for working with files represented as Virtual File (Virtual Files).
It serves the following main purposes:
Providing a universal API for working with files regardless of their actual location (on disk, in an archive, on an HTTP server, etc.)
Tracking file modifications and providing both old and new versions of the file content when a change is detected.
Providing a possibility to associate additional persistent data with a file in the VFS.
To provide the last two features, the VFS manages a persistent snapshot of some of the user's hard disk contents. The snapshot stores only those files which have been requested at least once through the VFS API, and is asynchronously updated to match the changes happening on the disk.
The snapshot is application level, not project level - so, if some file (for example, a class in the JDK) is referenced by multiple projects, only one copy of its contents will be stored in the VFS.
All VFS access operations go through the snapshot.
If some information is requested through the VFS APIs and is not available in the snapshot, it is loaded from disk and stored into the snapshot. If the information is available in the snapshot, the snapshot data is returned. The contents of files and the lists of files in directories are stored in the snapshot only if that specific information was accessed. Otherwise, only file metadata like name, length, timestamp, attributes are stored.
This means that the state of the file system and the file contents displayed in the IntelliJ Platform UI comes from the snapshot, which may not always match the disk's actual contents. For example, in some cases, deleted files can still be visible in the UI for some time before the deletion is picked up by the IntelliJ Platform.
The snapshot is updated from disk during refresh operations, which generally happen asynchronously. All write operations made through the VFS are synchronous - i.e., the contents are saved to disk immediately.
A refresh operation synchronizes the state of a part of the VFS with the actual disk contents. Refresh operations are explicitly invoked by the IntelliJ Platform or plugin code - i.e., when a file is changed on disk while the IDE is running, the change will not be immediately picked up by the VFS. The VFS will be updated during the next refresh operation, which includes the file in its scope.
IntelliJ Platform refreshes the entire project contents asynchronously on startup. By default, it performs a refresh operation when the user switches to it from another app. Still, users can turn this off via Settings | Appearance & Behavior | System Settings | Synchronize external changes\[...].
On Windows, Mac, and Linux, a native file watcher process is started that receives file change notifications from the file system and reports them to the IntelliJ Platform. If a file watcher is available, a refresh operation looks only at the files that have been reported as changed by the file watcher. If no file watcher is present, a refresh operation walks through all directories and files in the refresh scope.
Invoke internal action (Internal Actions Menu) Tools | Internal Actions | VFS | Show Watched VFS Roots to see all registered roots for current project.
Refresh operations are based on file timestamps. If a file's contents were changed, but its timestamp remained the same, the IntelliJ Platform will not pick up the updated contents.
There is currently no facility for removing files from the snapshot. If a file was loaded there once, it remains there forever unless it was deleted from the disk, and a refresh operation was called on one of its parent directories.
The VFS itself does not honor ignored files listed in Settings | Editor | File Types and folders to ignore and excluded folders listed in Project Structure | Modules | Sources | Excluded. If the application code accesses them, the VFS will load and return their contents. In most cases, the ignored files and excluded folders must be skipped from processing by higher-level code.
During the lifetime of a running instance of an IntelliJ Platform IDE, multiple VirtualFile instances may correspond to the same disk file. They are equal, have the same hashCode, and share the user data.
From the point of view of the caller, refresh operations can be either synchronous or asynchronous. In fact, the refresh operations are executed according to their own threading policy. The synchronous flag simply means that the calling thread will be blocked until the refresh operation (which will most likely run on a different thread) is completed.
Both synchronous and asynchronous refreshes can be initiated from any thread. If a refresh is initiated from a background thread, the calling thread must not hold a read action, because otherwise, a deadlock would occur. See IntelliJ Platform Architectural Overview (General Threading Rules) for more details on the threading model and read/write actions.
The same threading requirements also apply to functions like LocalFileSystem.refreshAndFindFileByPath() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/openapi/vfs/LocalFileSystem.java), which perform a partial refresh if the file with the specified path is not found in the snapshot.
In nearly all cases, using asynchronous refreshes is strongly preferred. If there is some code that needs to be executed after the refresh is complete, the code should be passed as a postRunnable parameter to one of the refresh methods:
RefreshQueue.createSession() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/openapi/vfs/newvfs/RefreshQueue.java)
VirtualFile.refresh() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java)
In some cases, synchronous refreshes can cause deadlocks, depending on which locks are held by the thread invoking the refresh operation.
All changes happening in the virtual file system, either due to refresh operations or caused by user actions, are reported as virtual file system events. VFS events are always fired in the event dispatch thread and in a write action.
The most efficient way to listen to VFS events is to implement BulkFileListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java) and to subscribe with it to the VirtualFileManager.VFS_CHANGES (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileManager.java) topic. A non-blocking variant AsyncFileListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java) is also available in 2019.2 or later. See How do I get notified when VFS changes? ("How do I get notified when VFS changes?" in "Virtual Files") for implementation details.
VFS listeners are application level and will receive events for changes happening in all the projects opened by the user. You may need to filter out events that aren't relevant to your task (e.g., via ProjectFileIndex.isInContent() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java)).
VFS events are sent both before and after each change, and you can access the old contents of the file in the before event. Note that events caused by a refresh are sent after the changes have already occurred on disk. So when you process the beforeFileDeletion event, for example, the file has already been deleted from disk. However, it is still present in the VFS snapshot, and you can access its last contents using the VFS API.
Note that a refresh operation fires events only for changes in files that have been loaded in the snapshot. For example, if you accessed a VirtualFile for a directory but never loaded its contents using VirtualFile.getChildren() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java), you may not get fileCreated notifications when files are created in that directory.
If you loaded only a single file in a directory using VirtualFile.findChild(), you will get notifications for changes to that file, but you may not get created/deleted notifications for other files in the same directory.
A VirtualFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java) (VF) is the IntelliJ Platform's representation of a file in a Virtual File System (VFS) (Virtual File System).
Most commonly, a virtual file is a file in a local file system. However, the IntelliJ Platform supports multiple pluggable file system implementations, so virtual files can also represent classes in a JAR file, old revisions of files loaded from a version control repository, and so on.
The VFS level deals only with binary content. Contents of a VirtualFile are treated as a stream of bytes, but concepts like encodings and line separators are handled on higher system levels.
Typical file operations are available, such as traverse the file system, get file contents, rename, move, or delete. Recursive iteration should be performed using VfsUtilCore.iterateChildrenRecursively() to prevent endless loops caused by recursive symlinks.
The VFS is built incrementally by scanning the file system up and down, starting from the project root. VFS refresh operations detect new files appearing in the file system. A refresh operation can be initiated programmatically using VirtualFileManager.syncRefresh()/asyncRefresh() or VirtualFile.refresh(). VFS refreshes are also triggered whenever file system watchers receive file system change notifications.
Invoking a VFS refresh might be necessary for accessing a file that has just been created by an external tool through the IntelliJ Platform APIs.
A particular file on disk is represented by equal VirtualFile instances for the IDE process's entire lifetime. There may be several instances corresponding to the same file, and they can be garbage-collected. The file is a UserDataHolder, and the user data is shared between those equal instances. If a file is deleted, its corresponding VirtualFile instance becomes invalid (isValid() returns false), and operations cause exceptions.
Usually, you don't. As a general rule, files are created either through the PSI API or through the regular java.io.File API.
If one needs to create a file through VFS, use VirtualFile.createChildData() to create a VirtualFile instance and VirtualFile.setBinaryContent() to write some data to the file.
See Virtual file system events ("Virtual File System Events" in "Virtual File System") for important details.
Implement BulkFileListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java) and subscribe to the message bus (Messaging Infrastructure) topic VirtualFileManager.VFS_CHANGES. For example:
project.getMessageBus().connect().subscribe(VirtualFileManager.VFS_CHANGES,
new BulkFileListener() {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
// handle the events
}
});See Message Infrastructure (Messaging Infrastructure) and Plugin Listeners (Listeners) for more details.
For a non-blocking alternative, starting with version 2019.2 of the platform, see AsyncFileListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/AsyncFileListener.java).
VfsUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/openapi/vfs/VfsUtil.java) and VfsUtilCore (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VfsUtilCore.java) provide utility methods for analyzing files in the Virtual File System.
For storing a large set of Virtual Files, use dedicated VfsUtilCore.createCompactVirtualFileSet().
Use ProjectLocator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/ProjectLocator.kt) to find the projects that contain a given virtual file.
To provide an alternative file system implementation (for example, an FTP file system), implement the VirtualFileSystem (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFileSystem.java) class (most likely you'll also need to implement VirtualFile), and register your implementation via com.intellij.virtualFileSystem extension point (2019.2 and later) or application component (Components) for earlier versions.
To hook into operations performed in the local file system (for example, when developing a version control system integration that needs custom rename/move handling), implement LocalFileOperationsHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/openapi/vfs/LocalFileOperationsHandler.java) and register it via LocalFileSystem.registerAuxiliaryFileOperationsHandler().
See Virtual File System (Virtual File System) for a detailed description of the VFS architecture and usage guidelines.
A Document (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/editor/Document.java) is an editable sequence of Unicode characters, typically corresponding to the text contents of a virtual file (Virtual Files).
Line breaks in a document are always normalized to \n. The IntelliJ Platform handles encoding and line break conversions when loading and saving documents transparently.
You may perform any operations that access or modify the file contents on the "plain text" level (as a sequence of characters, not as a tree of Program Structure Interface (PSI) elements).
Document instances are created when some operation needs to access the text contents of a file (in particular, this is necessary to build the PSI for a file). Also, document instances not linked to any Virtual Files can be created temporarily, for example, representing the contents of a text editor field in a dialog.
Document instances are weakly referenced from VirtualFile instances. Thus, an unmodified Document instance can be garbage-collected if no one references it, and a new instance is created if the document contents are reaccessed later.
Storing Document references in long-term data structures of a plugin will cause memory leaks.
For creating a new file on disk, please do not create a Document but a PSI file and get its Document (see "How do I create a PSI file?" in "PSI Files"). To create a Document instance that isn't bound to anything, use EditorFactory.createDocument() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/EditorFactory.java).
Document.addDocumentListener() allows receiving notifications about changes in a particular Document instance.
EditorFactory.getEventMulticaster().addDocumentListener() allows receiving notifications about changes in all open documents.
Register FileDocumentManagerListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/fileEditor/FileDocumentManagerListener.java) listener (Listeners) or subscribe to AppTopics.FILE_DOCUMENT_SYNC on any level bus to receive notifications when a Document is saved or reloaded from disk.
The general read/write action rules are in effect (see General Threading Rules). Besides, any operations which modify the contents of the document must be wrapped in a command (CommandProcessor.executeCommand() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/command/CommandProcessor.java)). executeCommand() calls can be nested, and the outermost executeCommand() call is added to the undo stack. If multiple documents are modified within a command, undoing this command will, by default, show a confirmation dialog to the user.
If the file corresponding to a Document is read-only (for example, not checked out from the version control system), document modifications will fail. Thus, before modifying the Document, it is necessary to call ReadonlyStatusHandler.ensureFilesWritable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/ReadonlyStatusHandler.java) to check out the file.
All text strings passed to Document modification methods (setText(), insertString(), replaceString()) must use only \n as line separators.
See also Working with Text ("Safely Replacing Selected Text in the Document" in "1. Working with Text") in Editors Basics tutorial.
DocumentUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/util/DocumentUtil.java) contains utility methods for Document processing. This allows you to get information like the text offsets of particular lines. This is particularly useful when you need text location/offset information about a given PsiElement.
This section covers working with text in the IntelliJ Platform editor.
This tutorial will lead you through a series of steps showing how to work with the IntelliJ Platform Editor, how to access and modify text it contains, and how to handle events sent to the editor.
The part of the API described in this tutorial only allows operations with "plain text". For operations that require access to the PSI, please see Program Structure Interface (PSI).
The following are referenced in the tutorial:
The editor_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/) plugin code sample,
editor-ui-api package (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api),
Those not found in editor-ui-api package:
EditorActionManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionManager.java),
EditorActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java),
TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java),
Related topics:
This tutorial shows how to use actions to access a caret placed in a document open in an editor. Using information about the caret, replace selected text in a document with a string.
The approach in this tutorial relies heavily on creating and registering actions. To review the fundamentals of creating and registering actions, refer to the Actions Tutorial (Actions Tutorial).
Multiple examples are used from the editor_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics) plugin code sample from the IntelliJ Platform SDK. It may be helpful to open that project in an IntelliJ Platform-based IDE, build the project, run it, select some text in the editor, and invoke the Editor Replace Text menu item on the editor context menu.

In this example, we access the Editor from an action. The source code for the Java class in this example is EditorIllustrationAction (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/src/main/java/org/intellij/sdk/editor/EditorIllustrationAction.java).
To register the action, we must add the corresponding elements to the <actions> ("actions" in "Plugin Configuration File") section of the plugin configuration file plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/src/main/resources/META-INF/plugin.xml). For more information, refer to the Registering Actions ("Registering a Custom Action" in "Creating Actions") section of the Actions Tutorial. The EditorIllustrationAction action is registered in the group EditorPopupMenu so it will be available from the context menu when focus is on the editor:
<action
id="EditorBasics.EditorIllustrationAction"
class="org.intellij.sdk.editor.EditorIllustrationAction"
text="Editor Replace Text"
description="Replaces selected text with 'Replacement'."
icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
</action>To determine conditions by which the action will be visible and available requires EditorIllustrationAction to override the AnAction.update() method. For more information, refer to Extending the Update Method ("Extending the update() Method" in "Creating Actions") section of the Actions Tutorial.
To work with a selected part of the text, it's reasonable to make the menu action available only when the following requirements are met:
There is a Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) object,
There is an instance of Editor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/Editor.java) available,
There is a text selection in Editor.
Additional steps will show how to check these conditions through obtaining instances of Project and Editor objects, and how to show or hide the action's menu items based on them.
Using the AnActionEvent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnActionEvent.java) event passed into the update method, a reference to an instance of the Editor can be obtained by calling getData(CommonDataKeys.EDITOR). Similarly, to obtain a project reference, we use the getProject() method.
public class EditorIllustrationAction extends AnAction {
@Override
public void update(@NotNull AnActionEvent event) {
// Get required data keys
Project project = event.getProject();
Editor editor = event.getData(CommonDataKeys.EDITOR);
// ...
}
}Note: There are other ways to access an Editor instance:
If a DataContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-ui/src/openapi/actionSystem/DataContext.java) object is available: CommonDataKeys.EDITOR.getData(context);
If only a Project object is available, use FileEditorManager.getInstance(project).getSelectedTextEditor()
After making sure a project is open, and an instance of the Editor is obtained, we need to check if any selection is available. The SelectionModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/SelectionModel.java) interface is accessed from the Editor object. Determining whether some text is selected is accomplished by calling the SelectionModel.hasSelection() method. Here's how the EditorIllustrationAction.update(AnActionEvent event) method should look:
public class EditorIllustrationAction extends AnAction {
@Override
public void update(@NotNull AnActionEvent event) {
// Get required data keys
Project project = event.getProject();
Editor editor = event.getData(CommonDataKeys.EDITOR);
// Set visibility only in the case of
// existing project editor, and selection
event.getPresentation().setEnabledAndVisible(project != null
&& editor != null && editor.getSelectionModel().hasSelection());
}
}Note: Editor also allows access to different models of text representation. The model classes are located in editor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor), and include:
FoldingModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/FoldingModel.java),
IndentsModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/IndentsModel.java),
ScrollingModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/ScrollingModel.java),
Based on the evaluation of conditions by EditorIllustrationAction.update(), the EditorIllustrationAction action menu item is visible. To make the menu item do something, the EditorIllustrationAction class must override the AnAction.actionPerformed() method. As explained below, this will require the EditorIllustrationAction.actionPerformed() method to:
Gain access to the document.
Get the character locations defining the selection.
Safely replace the contents of the selection.
Modifying the selected text requires an instance of the Document (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/editor/Document.java) object, which is accessed from the Editor object. The Document (Documents) represents the contents of a text file loaded into memory and opened in an IntelliJ Platform-based IDE editor. An instance of the Document will be used later when a text replacement is performed.
The text replacement will also require information about where the selection is in the document, which is provided by the primary Caret object, obtained from the CaretModel. Selection information is measured in terms of Offset ("Caret Offset" in "2. Editor Coordinates System. Positions and Offsets"), the count of characters from the beginning of the document to a caret location.
Text replacement could be done by calling the Document object's replaceString() method. However, safely replacing the text requires the Document to be locked and any changes performed in a write action. See the Threading Issues (General Threading Rules) section to learn more about synchronization issues and changes safety on the IntelliJ Platform. This example changes the document within a WriteCommandAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/command/WriteCommandAction.java).
The complete EditorIllustrationAction.actionPerformed() method is shown below:
Note the selection in the document is replaced by a string using a method on the Document object, but the method call is wrapped in a write action.
After the document change, the new text is de-selected by a call to the primary caret.
public class EditorIllustrationAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
// Get all the required data from data keys
Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
Project project = event.getRequiredData(CommonDataKeys.PROJECT);
Document document = editor.getDocument();
// Work off of the primary caret to get the selection info
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
int start = primaryCaret.getSelectionStart();
int end = primaryCaret.getSelectionEnd();
// Replace the selection with a fixed string.
// Must do this document change in a write action context.
WriteCommandAction.runWriteCommandAction(project, () ->
document.replaceString(start, end, "editor_basics")
);
// De-select the text range that was just replaced
primaryCaret.removeSelection();
}
}The previous tutorial Working with Text (1. Working with Text) demonstrated how to use actions to access a caret placed in a document open in an editor. The examples replaced selected text in a document by using information about the caret.
Every caret has a set of properties describing its position in one of several coordinate systems. This tutorial describes how to access information about the caret(s) in an editor.
In this tutorial, the editor_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics) code sample is used to explore caret positions. In particular, the Caret Position action added by editor_basics to the editor context menu is used to retrieve information about the current caret position. A keyboard shortcut can also initiate the action.

The source code for the Java class behind the menu action is EditorAreaIllustration (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/src/main/java/org/intellij/sdk/editor/EditorAreaIllustration.java). The focus of discussion will be the EditorAreaIllustration.actionPerformed() method. For more information about creating action classes, see the Actions Tutorial (Actions Tutorial), which covers the topic in depth.
The properties of a caret can be accessed by obtaining an instance of the CaretModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/CaretModel.java) object. As in the Working with Text (1. Working with Text) tutorial, the AnActionEvent is used to get the Editor object. The Editor object provides access to the CaretModel object, as shown below:
public class EditorAreaIllustration extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
// Get access to the editor and caret model. update() validated editor's existence.
Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
}
}When a Document is opened, the Editor assigns an internal, zero-based coordinate system to lines and columns in the Document. The first line in a Document and the first character in each line are assigned the zero position. Every character in a Document is assigned an Offset, which is a zero-based count of the characters from the beginning of the file to that character. These LogicalPosition coordinates are used to describe the line and column number for a caret position. Note that the Logical Position coordinate system is different from the editor UI, which is one-based rather than zero-based.
Logical Position coordinates and other coordinate systems discussed in this tutorial can be used to characterize any location in an Editor, not just carets. Hints used for code insights are characterized in terms of these coordinates, for example HintManager.getHintPosition() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/codeInsight/hint/HintManagerImpl.java). Custom visual elements displayed in an Editor, called Inlay (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/Inlay.java) objects, are also expressed in terms of these coordinate systems.
The diagram below shows the Logical Position coordinate system applied to some example content. The character "s" in the red box represents placing the cursor on that character. It has the caret position of line 1, column 9, and Offset 28. More about caret Offsets is discussed below.

The Multiple Carets (Multiple Carets) documentation covers the subject of more than one caret in an Editor. For this tutorial, be aware there may be more than one caret in an Editor at any given time. Consequently, examples use the Primary Caret in an Editor. If there is only one caret in an Editor, it is the Primary Caret. For the case of multiple carets in an Editor, the Primary Caret is the one on which query and update methods in the model operate at the moment.
The caret Logical Position is a zero-based, (line and column) position of the caret in the Editor. Logical Position information is obtained from the LogicalPosition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/LogicalPosition.java) object for that caret.
The Logical Position line number of a caret ignores the effects of settings that change the presentation of a Document within the Editor. Examples of these settings are Code (Line) Folding (https://www.jetbrains.com/help/idea/working-with-source-code.html#code_folding) and Soft Line Wrap (https://www.jetbrains.com/help/idea/using-code-editor.html#f804afd8). These effects mean regardless of whether one or more lines in an Editor are folded or soft-wrapped, the caret Logical Position line number will not change.
In the example Java file below, Logical Position line numbers 1-3 are folded into line 0. The caret - a blue block - is placed on the letter "p" in "public." Using the editor_basics Caret Position action to inspect the caret, it is reported to be at Logical Position (5,0) - which is line 5, character 0 - the first character in the line. This means that caret Logical Position is not changed by Code Folding:

However, note that applying Code Folding does change the reported Visual Position of the caret even if the Logical Position stays constant. More about Visual Position is discussed below. However, it's clear combinations of Code Folding and Soft Wrap means that one Logical Position of a caret could map to multiple Visual Positions. The Editor interface provides methods to work with a caret Logical and Visual Position, such as the method Editor.logicalToVisualPosition().
A caret's Visual Position differs from Logical Position in that it takes into account editor presentation settings such as Code Folding and Soft Line Wrap. In doing so, VisualPosition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/VisualPosition.java) counts - zero-based - the lines of a Document that can be displayed in an Editor. Consequently, Visual Positions can't be uniquely mapped to Logical Positions or corresponding lines in the underlying Document.
For example, Soft Line Wrap affects the Visual Position of succeeding lines. In the image below, Soft Line Wrap is applied to Logical line three. With the caret placed at the same character location as in previous tests, it is evident the Logical Position has not changed. However, the Visual Position line number has increased by one! The comments on each line illustrate how the Soft Wrap portion of Logical line three is evaluated as Visual Position line four, as though it was a separate line.

The Logical and Visual Position objects for a caret are obtained from the Caret (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/Caret.java) object, as shown in the code snippet below.
public class EditorAreaIllustration extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
// Get access to the editor and caret model.
Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
Caret primaryCaret = caretModel.getPrimaryCaret();
LogicalPosition logicalPos = primaryCaret.getLogicalPosition();
VisualPosition visualPos = primaryCaret.getVisualPosition();
}
}The Column Position is a count of characters from the beginning of a Logical (Position) line to the current caret position in that line. Characters are counted using a zero-based numbering system, so the first character of a line is numbered zero. Note that Column Position is different from the editor UI, which uses a one-based numbering scheme.
Column Position includes:
Whitespace, such as tabs. Tabs can occupy multiple columns, up to the tab size set for the editor.
The character selected by the caret.
The Column Position of a caret is the boundary between two characters. A caret can be associated with either a preceding or succeeding character. The association is important in bidirectional text, where mapping from Logical Column Position to Visual Column Position is not continuous.
As defined in the LogicalPosition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/LogicalPosition.java) class, if a caret position is associated with a succeeding character it Leans Forward. Otherwise, it is associated with the preceding character.
As defined in the VisualPosition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/VisualPosition.java) class, if a caret position is associated with a succeeding character it Leans Right. Otherwise, it is associated with the preceding character.
In the example below, placing a (blue) block caret on the first visible character in Logical line three produces a column position of 0 for both Visual and Logical Positions. Note that the text is unidirectional in this example. In the Logical Position the caret leans forward, meaning it is associated with the succeeding character in the Logical line. For the Visual Position the caret leans right, indicating its association with the succeeding character in the Visual line.

Consider the Java snippet below, and use the editor_basics Caret Position action to report caret information at each step. Be sure to use the keyboard shortcut to invoke the action so that the caret position is not disturbed.
The line containing the String variable declaration contains bidirectional text. Starting on the left end of the line, and using the arrow key to advance a line-shaped cursor to between the (" characters reveals discontinuities in caret coordinate column positions.
After the caret first moves from between g( to between (", the Logical and Visual Positions have equal column positions of 26, and neither leans. Note the value of the caret positions you measure may have different starting values because of line indentation, but the sign and magnitude of the changes in position will be the same.
Advancing the caret once more does not appear to visually move the cursor. However, the Logical Position column increases to 59, and although the Visual Position column does not change, it leans right.
Continuing to advance the cursor (to the right) through the string causes the Logical Position column to decrease, and the Visual Position column to increase.
Once the cursor advances to between the ". characters, the Logical Position column position is again 26, and leans forward. The Visual Position column position is now 59.
Moving the caret right once more does not appear to visually advance the cursor. However, the Logical Position and Visual Position column values are both 59, and both lean.
As the cursor advances to the right, both Logical and Visual column values increase.
public void showNow() {
//234567890123456789012345678901234567890123456789012345678901234567890
String str = new String("تعطي يونيكود رقما فريدا لكل حرف".getBytes(), java.nio.charset.StandardCharsets.UTF_8);
System.out.println(str);
}The apparent discontinuity in Logical Position is because the RTL portion of the string is treated (or counted) in the logical character order in which it would be written. The apparent continuity in Visual Position is because the RTL portion of the string is counted in the visual order in which it is displayed in the code.
The Offset of a caret is a character count from the beginning of a Document to the caret position. Caret offsets are always calculated in terms of Logical Position. The caret Offset includes:
The first (0th) character in a document.
Whitespace characters, including newline and tabs.
Any characters after end-of-line if the IDE settings permit them. (Settings | Editor | General | Virtual Space)
The character selected by the caret.
The example below demonstrates the Offset of a caret placed at the first character of Logical line one. Note the Offset is 22, which is one greater than the number of visible characters on line zero, and the first character on line one. This apparent discrepancy is actually correct because the Offset includes the newline character for line zero.

To display the values of caret Logical and Visual positions, and Offset, a Messages.showInfoMessage() method shows them in the form of notification as the action is performed.
public class EditorAreaIllustration extends AnAction {
public void actionPerformed(@NotNull AnActionEvent event) {
// Get access to the editor and caret model.
Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
CaretModel caretModel = editor.getCaretModel();
// Getting the primary caret ensures we get the correct one of a possible many.
Caret primaryCaret = caretModel.getPrimaryCaret();
// Get the caret information
LogicalPosition logicalPos = primaryCaret.getLogicalPosition();
VisualPosition visualPos = primaryCaret.getVisualPosition();
int caretOffset = primaryCaret.getOffset();
// Build and display the caret report.
String report = logicalPos.toString() + "\n" +
visualPos.toString() + "\n" +
"Offset: " + caretOffset;
Messages.showInfoMessage(report, "Caret Parameters Inside The Editor");
}
}The previous tutorial Editor Coordinate Systems (2. Editor Coordinates System. Positions and Offsets) described working with caret coordinate systems in an editor window. Caret position was discussed in terms of Logical Position, Visual Position, and Offset. This tutorial introduces the Editor Action system, which handles actions activated by keystroke events in the editor. Two classes from the editor_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics) code sample are used to illustrate:
Using an IntelliJ Platform EditorActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java) to manipulate a caret.
Creating and registering a custom TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java) to intercept keystrokes and change the document.
In this portion of the tutorial, the editor_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics) code sample is used to demonstrate cloning an existing caret. A custom action class will use EditorActionManager to access a specific EditorActionHandler for caret cloning. The editor_basics code sample adds an Editor Add Caret menu item to the editor context menu:

The source code for the Java action class is EditorHandlerIllustration (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/src/main/java/org/intellij/sdk/editor/EditorHandlerIllustration.java), a subclass of AnAction. For more information about creating action classes, see the Actions Tutorial (Actions Tutorial), which covers the topic in depth.
The EditorHandlerIllustration action is registered in the editor_basic plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/editor_basics/src/main/resources/META-INF/plugin.xml) file. Note that this action class is registered to appear on the Editor context menu.
<actions>
<action
id="EditorBasics.EditorHandlerIllustration"
class="org.intellij.sdk.editor.EditorHandlerIllustration"
text="Editor Add Caret"
description="Adds a second caret below the existing one."
icon="SdkIcons.Sdk_default_icon">
<add-to-group group-id="EditorPopupMenu" anchor="first"/>
</action>
</action>Under what conditions should the EditorHandlerIllustration action be capable of cloning a caret? Only if the following conditions are met in the EditorHandlerIllustration.update() method:
A project is open,
An editor is available,
There is at least one caret active in the editor.
After ensuring that Project and Editor objects are available, the Editor object is used to verify there is at least one caret:
public class EditorHandlerIllustration extends AnAction {
@Override
public void update(@NotNull AnActionEvent event) {
Project project = event.getProject();
Editor editor = event.getData(CommonDataKeys.EDITOR);
// Make sure at least one caret is available
boolean menuAllowed = false;
if (editor != null && project != null) {
// Ensure the list of carets in the editor is not empty
menuAllowed = !editor.getCaretModel().getAllCarets().isEmpty();
}
event.getPresentation().setEnabledAndVisible(menuAllowed);
}
}When the EditorHandlerIllustration.actionPerformed() method clones the caret, it should use the appropriate IntelliJ Platform EditorActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java). An instance of EditorActionManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionManager.java) is required to obtain the correct EditorActionHandler. The EditorActionManager class provides a static method to do this.
To request the correct EditorActionHandler from EditorActionManager, consult the IdeActions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/actionSystem/IdeActions.java) interface for the correct constant to pass into the EditorActionManager.getActionHandler() method. For cloning a caret below the primary caret, the constant is ACTION_EDITOR_CLONE_CARET_BELOW. Based on that constant, the EditorActionManager returns an instance of CloneCaretActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java), a subclass of EditorActionHandler.
// Snippet from EditorHandlerIllustration.actionPerformed()
EditorActionManager actionManager = EditorActionManager.getInstance();
EditorActionHandler actionHandler =
actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW);To clone the caret requires only calling the EditorActionHandler.execute() method and passing in the appropriate context.
public class EditorHandlerIllustration extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
Editor editor = event.getRequiredData(CommonDataKeys.EDITOR);
EditorActionManager actionManager = EditorActionManager.getInstance();
EditorActionHandler actionHandler =
actionManager.getActionHandler(IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW);
actionHandler.execute(editor,
editor.getCaretModel().getPrimaryCaret(), event.getDataContext());
}
}The TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java) interface is the basis for classes that handle keystroke events from the editor. Custom implementations of the class are registered to handle editor keystroke events, and receive a callback for each keystroke. The steps below explain how to use TypedActionHandler to customize the behavior of the editor when keystroke events are received.
First, a subclass such as MyTypedHandler is created based on TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java). The class overrides the method TypedActionHandler.execute(), which is the callback for editor keystroke events.
Override the TypedActionHandler.execute() method in MyTypedHandler to implement the logic for handling keystroke events. This method is called every time a key is pressed when the Editor Tool Window has focus.
In the following example, the MyTypedHandler.execute() method inserts "editor_basics\n" at the zero caret Offset ("Caret Offset" in "2. Editor Coordinates System. Positions and Offsets") position when a keystroke event occurs. As explained in Working with Text ("Safely Replacing Selected Text in the Document" in "1. Working with Text"), safe modifications to the document must be in the context of a write action. So although a method on the Document interface does the String insertion, the write action ensures a stable context.
final class MyTypedHandler implements TypedActionHandler {
@Override
public void execute(@NotNull Editor editor,
char c,
@NotNull DataContext dataContext) {
Document document = editor.getDocument();
Project project = editor.getProject();
Runnable runnable = () -> document.insertString(0, "editor_basics\n");
WriteCommandAction.runWriteCommandAction(project, runnable);
}
}A custom implementation of TypedActionHandler must be registered to replace the existing typing handler to receive editor keystroke events. The registration is done through the TypedAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedAction.java) class.
As is shown in the snippet below, the EditorActionManager is used to get access to the TypedAction class. The method TypedAction.setupHandler() is used to register the custom MyTypedHandler class:
public class EditorHandlerIllustration extends AnAction {
static {
EditorActionManager actionManager = EditorActionManager.getInstance();
TypedAction typedAction = actionManager.getTypedAction();
typedAction.setupHandler(new MyTypedHandler());
}
}Placing the registration code in the EditorHandlerIllustration class is somewhat arbitrary in the sense that the registration of MyTypedHandler has nothing to do with the EditorHandlerIllustration class. However, the EditorHandlerIllustration class is convenient because as an action it gets instantiated at application startup. On instantiation, the static block of code in EditorHandlerIllustration gets evaluated. In the editor_basics code sample any of the AnAction derived classes would work for registering MyTypedHandler.
Implementing ExtendWordSelectionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/ExtendWordSelectionHandler.java) and registering it in com.intellij.extendWordSelectionHandler EP in your plugin.xml (Plugin Configuration File) allows you to provide additional text ranges to be used when extending or shrinking a selection. Return true from canSelect(PsiElement) for the PSI elements that you want to provide additional text-ranges for. The IntelliJ Platform will call select(PsiElement, CharSequence, int, Editor) for these elements where you can compute additional text ranges and return them as List<TextRange>.
See also:
The two actions Extend Selection and Shrink Selection (https://www.jetbrains.com/help/idea/working-with-source-code.html#editor_code_selection) in IntelliJ Platform IDEs let you adjust selected text based on the structure of the source code. This makes it easy to select not only expressions, blocks, and function definitions, but also code like whole lines or tags in Javadoc comments.
When implementing a custom language, the IntelliJ Platform provides basic implementations of this EP, allowing you to select code based on your PSI structure and to select whole lines. In many cases this is sufficient to provide a good user experience. However, sometimes it's advantageous to provide additional regions that the user may wish to be able to select when extending or shrinking a selection.
This EP has two methods that need to be implemented:
canSelect(PsiElement) is called on each PSI element, starting from the element at the cursor and walking up each of its parents. Return true for a particular element to indicate that further text-ranges should be included for the PSI element.
select(PsiElement, CharSequence, int, Editor) returns the text-ranges within the PSI element of interest are calculated and returned.
A possible use-case for custom language developers is a function call f(a, b) where the function call node has its two arguments as children. If the cursor is located at argument a, extending the selection would first select argument a itself and in the next step grow to cover the whole function call. However, you might want to select the list of all arguments as an intermediate step. This can be achieved by implementing this EP in the following way:
Create a class that implements the ExtendWordSelectionHandler interface and register it in a com.intellij.extendWordSelectionHandler EP in your plugin.xml.
The canSelect(PsiElement) method should return true for the function call node. That indicates that select(PsiElement, CharSequence, int, Editor) will be called for the function-call node.
When the select() method is called, you can use the function call PSI element or the editor text to extract the text range that spans the arguments a and b and return it as List<TextRange> with one element.
Looking at other implementations can be an effective way to get a better understanding of how this EP works. To get further insight into this EP, you may want to take a look at DocTagSelectioner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-frontback-impl/src/com/intellij/codeInsight/editorActions/wordSelection/DocTagSelectioner.java). It provides the ability to select tag names like @param in Javadoc comments. Additionally, the IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.extendWordSelectionHandler) provides a list of open-source plugins with implementations of the com.intellij.extendWordSelectionHandler EP.
There are also some important places in the IntelliJ Platform to add breakpoints during debugging. When Extend Selection is called by the user, it is handled by SelectWordHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordHandler.java). The majority of the work, however, is then done inside SelectWordUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SelectWordUtil.java), where processElement() checks which implementations of this EP apply for the current PSI element. If one of them returns true from its canSelect() method, the additional text ranges are extracted in the askSelectioner() function. These places are good candidates to set breakpoints and investigate during debugging.
Product Help: Multiple cursors and selection ranges (https://www.jetbrains.com/help/idea/multicursor.html)
Most editor actions (keyboard navigation, text insertion and deletion, etc.) will be applied to each caret independently. Each caret has its own associated selection, which is a continuous range of document characters (can be empty). When after some action two or more carets end up in the same visual position, they are merged into a single caret with their associated selections merged into a single one. A similar thing will happen when selections for several carets become overlapped: only one of the carets will remain, and the selections will be merged.
There's a concept of primary caret — the one on which non-multi-caret-aware actions and the actions which need a single-point document context (like code completion) will operate. Currently, the most recent caret is considered the primary one.
Core logic related to multi-caret implementation such as accessing currently existing carets, adding and removing carets, is available via CaretModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/CaretModel.java). For text selection, see SelectionModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/SelectionModel.java).
Methods in CaretModel and SelectionModel to query and modify caret and selection positions work by default on the primary caret. In the context of CaretModel.runForEachCaret() method though, they operate on the current caret.
SelectionModel.getBlockSelectionStarts() and getBlockSelectionEnds() work in multi-caret state, returning all selected regions.
When EditorActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/EditorActionHandler.java) is invoked, an additional parameter will be passed to it: a caret instance on which it should operate, or null if it's invoked without any caret context. If the handler invokes another handler (delegate handler for the same actionId or a completely unrelated handler), that parameter should normally be passed to the delegate unchanged (unless no context caret has been provided to the handler, but it needs to invoke another handler on a specific caret). Of course, the handler can just ignore the caret parameter if its functionality is not related to caret/selection position.
If the handler needs to implement multi-caret functionality it can do so explicitly in the overridden doExecute method, but if it just needs that method to be invoked for each caret, it suffices to pass a parameter to EditorActionHandler constructor to make doExecute called for each caret when the handler is invoked without a specific caret context.
The following delegates are available:
EnterHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterHandlerDelegate.java)
BackspaceHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/BackspaceHandlerDelegate.java)
JoinLinesHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/JoinLinesHandlerDelegate.java)
EditorNavigationDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/EditorNavigationDelegate.java)
SmartEnterProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java)
CommentCompleteHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CommentCompleteHandler.java)
StatementUpDownMover (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/moveUpDown/StatementUpDownMover.java)
CodeBlockProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/CodeBlockProvider.java)
There is no need to make any changes in the handlers to support multiple carets — they are already invoked for each caret.
TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java) and TypedHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/TypedHandlerDelegate.java) implementations are invoked only once for each typed character. If those handlers need to support multiple carets, they will need to implement that explicitly.
EditorModificationUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/EditorModificationUtil.java). typeInStringAtCaretHonorMultipleCarets() method is available to do the most common task in this case: inserting the same text into all caret positions and/or moving all carets relatively to their current position. Examples of its usage:
XmlGtTypedHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/codeInsight/editorActions/XmlGtTypedHandler.java).
TypedHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/TypedHandlerDelegate.java) implementations are invoked automatically for each caret. If one wants to implement custom multicaret behaviour on typing, TypedActionHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/editor/actionSystem/TypedActionHandler.java) needs to be provided instead.
Existing actions inheriting from CodeInsightAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/actions/CodeInsightAction.java) will work for primary caret only. To support multiple carets, one should subclass MultiCaretCodeInsightAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/actions/MultiCaretCodeInsightAction.java) instead. Each caret might have a different editor and PSI instance, so using the old API is not possible.
The IntelliJ Platform Execution API allows running external processes (https://www.jetbrains.com/help/idea/running-applications.html) from within the IDE, e.g., applications, tests, servers, scripts, etc. These processes can be run from the editor (Editors), project view (Project View), run toolbar, or custom actions (Actions Tutorial). Executed processes can be stopped, restarted, and their output and logs can be viewed in the run console. It is possible to manage and persist Run Configurations (https://www.jetbrains.com/help/idea/run-debug-configuration.html) from the UI.
The key Execution API classes are divided into two groups:
Configuration
Execution
The following diagram shows the relations between the key classes:
RunProfile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunProfile.java) is a base interface for things that can be executed (e.g., an application started via a main() method, a test or test suite, a remote debug session to a specific host, etc.). It is responsible for building a process that is run and creates a RunProfileState.
RunProfileState (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunProfileState.java) holds the information about the process ready to be started, like the command line, current working directory, and environment variables. The existence of RunProfileState allows extensions and other components to patch the run profile and modify the parameters before the process gets executed. See standard run profile state classes.
RunConfiguration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfiguration.java) is a specific type of run profile, which can be managed and persisted by users from UI. See the Run Configurations (Run Configurations) section for more details.
Executor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/Executor.java) describes a specific way of executing run profiles. The three default executors provided by the IntelliJ Platform are:
DefaultRunExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/executors/DefaultRunExecutor.java) (Run)
DefaultDebugExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/xdebugger-api/src/com/intellij/execution/executors/DefaultDebugExecutor.java) (Debug)
CoverageExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage-common/src/com/intellij/coverage/CoverageExecutor.java) (Run with Coverage)
Custom implementations are rarely required (it may be necessary, e.g., when a profiler integration is implemented). Executor implementations are registered in the com.intellij.executor extension point (EP).
ProgramRunner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/runners/ProgramRunner.java) is responsible for the execution workflow of a RunProfile with a certain Executor. ProgramRunner implementations are registered in the com.intellij.programRunner EP.
ExecutionEnvironment (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/runners/ExecutionEnvironment.java) object aggregates all the objects and settings required to execute the process. It is used by the ProgramRunner.execute() method.
RunContentBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/runners/RunContentBuilder.java) is responsible for building the content of the Run or Debug tool window. The content is built by the ProgramRunner executing the process.
ExecutionResult (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ExecutionResult.java) is prepared by RunProfileState class before execution and contains ExecutionConsole and ProcessHandler.
ExecutionConsole (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ui/ExecutionConsole.java) is a component displaying the result of executing the process. It can be a console, a test results view, or another similar component.
ProcessHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/execution/process/ProcessHandler.java) is an object attached to the executed process and allows controlling and accessing the information about the process. See standard process handler class.
A standard execution of a process consists of the following steps:
Depending on the execution context:
For execution initiated by a user from UI: the user selects a RunConfiguration (e.g., by choosing one from the run configurations combo box) and an Executor (e.g., by pressing the Run toolbar button).
For programmatic execution: a RunProfile instance is created, and an Executor is provided. Specific run executors can be accessed with ExecutorRegistry.getInstance().getExecutorById().
The ProgramRunner that will execute the process is selected. The first ProgramRunner that returns true from ProgramRunner.canRun(executorId, runProfile) is selected from all registered program runners.
The ExecutionEnvironment is created. It aggregates all the information required to run the process as well as the selected ProgramRunner, Executor, and RunProfile.
ProgramRunner.execute(executionEnvironment) is called, and the actual process is executed.
A standard implementation of ProgramRunner.execute() goes through the following steps to execute the process:
RunProfile.getState() method is called to create a RunProfileState object, which describes a process about to be started. The command line parameters, environment variables, and other information required to start the process are initialized at this stage.
RunProfileState.execute(executor, programRunner) is called. It starts the process, attaches a ProcessHandler to its input and output streams, creates a console to display the process output, and returns an ExecutionResult object aggregating the ExecutionConsole and the ProcessHandler.
The RunContentBuilder object is created and invoked to display the execution console in a Run or Debug tool window tab.
Example: RunAnythingCommandProvider.runCommand() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/actions/runAnything/activity/RunAnythingCommandProvider.java), which programmatically executes a command typed by a user in the Run Anything popup
The standard base class used as an implementation of RunProfileState is CommandLineState (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/CommandLineState.java). It contains the logic for putting together a running process and a console into an ExecutionResult but doesn't know anything about how the process is actually started. For starting the process, it's recommended to use the GeneralCommandLine (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-util-io/src/com/intellij/execution/configurations/GeneralCommandLine.java) class, which takes care of setting up the command line parameters and executing the process.
Alternatively, if the process is a JVM-based one, use the JavaCommandLineState (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/execution/impl/src/com/intellij/execution/configurations/JavaCommandLineState.java) base class. It handles the JVM command line parameters and can take care of details like calculating the classpath for the JVM. Note that using this class requires dependency (Plugin Dependencies) on the Java plugin ("Java" in "IntelliJ IDEA Plugin Development").
The standard class for monitoring the execution of a process and capturing its output is OSProcessHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-util-io/src/com/intellij/execution/process/OSProcessHandler.java). Once an instance of OSProcessHandler is created from either a GeneralCommandLine or a Process object, call the startNotify() method to capture its output. To display the exit status of the process in the console, attach a ProcessTerminatedListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/execution/process/ProcessTerminatedListener.java) to the OSProcessHandler.
When a process execution is handled with CommandLineState, a console view will be automatically created and attached to the process's output.
Alternatively, it can be handled with the following steps:
TextConsoleBuilderFactory.createBuilder(project).getConsole() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/filters/TextConsoleBuilderFactory.java) creates a ConsoleView (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ui/ConsoleView.java) instance.
ConsoleView.attachToProcess() attaches it to the output of a process.
If the running process uses ANSI escape codes to color its output (https://en.wikipedia.org/wiki/ANSI_escape_code#Colors), use the ColoredProcessHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-util-io/src/com/intellij/execution/process/ColoredProcessHandler.java) class to display the colors in the IDE console.
Console Filter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/filters/Filter.java) allows converting certain strings found in the process output to clickable hyperlinks. To attach a filter to the console, use CommandLineState.addConsoleFilters() or TextConsoleBuilder.addFilter() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/filters/TextConsoleBuilder.java) if the console is created manually. Two standard filter implementations are RegexpFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/filters/RegexpFilter.java) and UrlFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/filters/UrlFilter.java).
Console filters can be also provided by implementing ConsoleFilterProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/filters/ConsoleFilterProvider.java) and registering it in com.intellij.consoleFilterProvider EP.
Implement ExecutionListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ExecutionListener.java) and subscribe to ExecutionManager#EXECUTION_TOPIC (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ExecutionManager.kt). The listener allows for listening to several phases of the process execution lifecycle.
Product Help: Run/Debug Configuration (https://www.jetbrains.com/idea/help/run-debug-configuration.html)
A run configuration is a specific type of run profile ("Configuration Classes" in "Execution"). Run configurations can be managed from the UI and are persisted between IDE restarts. They allow users to specify execution options like a working directory, environment variables, program arguments, and other parameters required to run a process. Run configurations can be started from the Run toolbar, the editor, and executed programmatically from actions or other components.
The following diagram shows the key run configurations classes:
Original:
Run Configuration API (except SettingsEditor class, which is a class shared by many IntelliJ Platform APIs) is a part of the Execution API (Execution).
The entry point of a run configuration implementation is ConfigurationType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ConfigurationType.java). It is responsible for the run configuration type and instances presentation and contains configuration factories. A single configuration type can have multiple configuration factories, e.g., the Docker configuration type can create run configurations for:
Dockerfile
Docker Image
Docker-compose
To see the list of configuration types available in the IDE, go to Run \| Edit Configurations and click the Add button (+ icon).
ConfigurationType implementations are registered in the com.intellij.configurationType extension point (EP).
Standard base classes for configuration type implementations are:
SimpleConfigurationType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/runConfigurationType.kt) - used for configuration types that have a single configuration factory. Actually, this configuration type is also a configuration factory, and there is no need for setting up a factory.
ConfigurationTypeBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/runConfigurationType.kt) - used for configuration types that have multiple configuration factories. Factories should be added in the constructor by calling the addFactory() method.
Marking a configuration type as dumb aware ("DumbAware API" in "Indexing and PSI Stubs") makes all its configurations available during indexing.
Sometimes, it is required to provide run configurations programmatically from contexts external to run configuration UI. Implementing VirtualConfigurationType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/VirtualConfigurationType.java) blocks the possibility of adding and removing run configurations of this type in the Run/Debug Configurations panel. Editing its template is also not available.
ConfigurationFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ConfigurationFactory.java) classes are responsible for creating run configuration instances. The only method required to be implemented is createTemplateConfiguration(), which is called once for each project to create the run configuration template. The actual run configurations are created in the createConfiguration() method by cloning the template.
Configuration factory presentation is inherited from the containing configuration type. If customization is needed, override the presentation methods in the factory class.
By default, configurations created by a given factory are not editable in dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"). To enable editing them in Dumb Mode, return true from isEditableInDumbMode().
RunConfiguration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfiguration.java) extends RunProfile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunProfile.java) and represents a named profile that can be run by the Execution API (Execution).
When implementing a run configuration class, consider using one of the standard base classes:
RunConfigurationBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfigurationBase.java) - a general-purpose base class that contains the most basic implementation of a run configuration.
LocatableConfigurationBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/LocatableConfigurationBase.java) - a base class for configurations that can be created from context.
ModuleBasedConfiguration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ModuleBasedConfiguration.java) - a base class for a configuration that is associated with a specific Module (e.g., Java run configurations use the selected module to determine the run classpath).
A run configuration may allow editing its general settings and settings specific to a program runner ("Execution Classes" in "Execution"). If it is required, a RunConfiguration implementation should return a SettingsEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/SettingsEditor.java) instance from:
getConfigurationEditor() for editing run configuration settings
getRunnerSettingsEditor() for editing settings for a specific program runner
A SettingsEditor implementation must provide the following methods:
getComponent() - creates a UI component for displaying settings controls
applyEditorTo() - copies the current editor UI state into the target settings object
resetEditorFrom() - resets the current editor UI state to the initial settings state
In the case of run configuration settings, the settings object is RunConfiguration itself. Settings specific to a program runner must implement ConfigurationPerRunnerSettings (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ConfigurationPerRunnerSettings.java).
If the settings editor requires validation, implement CheckableRunConfigurationEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/impl/CheckableRunConfigurationEditor.java).
If the settings editor is complex, see for solutions.
Example: DemoSettingsEditor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/run_configuration/src/main/java/org/jetbrains/sdk/runConfiguration/DemoSettingsEditor.java) from the run_configuration code sample.
Run configuration settings are persistent. They are stored in the file system and loaded back after the IDE restart. Persisting and loading settings are performed by writeExternal() and readExternal() methods of RunConfiguration class correspondingly.
The actually stored configurations are represented by instances of the RunnerAndConfigurationSettings (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/RunnerAndConfigurationSettings.java) class, which combines a run configuration with runner-specific settings and stores general run configuration flags and properties.
If a plugin requires creating run configurations programmatically, .e.g, from a custom action, perform the following steps:
RunManager.createConfiguration() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/RunManager.kt) - creates an instance of RunnerAndConfigurationSettings.
RunManager.addConfiguration() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/RunManager.kt) - makes the created configuration persistent by adding it to either the list of shared configurations stored in a project or to the list of local configurations stored in the workspace file.
Run configurations can be created and run from context, e.g., by right-clicking an application main method, a test class/method, etc., directly in the editor or the project view. This is achieved by implementing LazyRunConfigurationProducer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/actions/LazyRunConfigurationProducer.kt) and registering it in com.intellij.runConfigurationProducer EP.
The extension requires implementing the following methods:
getConfigurationFactory() - returns the factory creating run configurations of the type specified in the extension class implementation.
setupConfigurationFromContext() - receives a blank configuration of the specified type and a ConfigurationContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/actions/ConfigurationContext.java) containing information about a source code location (accessible by calling getLocation() or getPsiLocation()). The implementation needs to check whether the location is applicable to the configuration type (e.g., if it is in a file of the supported language). If it is, put the correct context-specific settings into the run configuration and return true. Return false otherwise.
isConfigurationFromContext() - checks if a configuration was created from the specified context. This method allows reusing an existing run configuration, which applies to the current context, instead of creating a new one and possibly ignoring the user's customizations in the existing one.
If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs"). If the run configuration requires additional data before it is executed for the first time, override RunConfigurationProducer.onFirstRun() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/execution/actions/RunConfigurationProducer.java) to provide it or display UI to get the data from the user.
To support the automatic naming of configurations created from context, the configuration should extend LocatableConfigurationBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/LocatableConfigurationBase.java). It supports generating a name for a configuration from its settings and tracking whether the user changed the name.
If a run configuration is closely related to a PSI element (e.g., runnable method, test, etc.), it is possible to allow running configurations by clicking the editor gutter icon (https://www.jetbrains.com/help/idea/running-applications.html#run-from-editor). It is achieved by implementing RunLineMarkerContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/lineMarker/RunLineMarkerContributor.java), which provides information like the icon, tooltip content, and available actions for a given PSI element.
The standard method for providing the information is getInfo(). If computing the information is slow, implement getSlowInfo(), which is used by the editor highlighting mechanism to gather information in batch, and apply all the information at once to avoid icons blinking. If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
To provide the standard executor actions like Run, Debug, etc., use ExecutorAction.getActions() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/ExecutorRegistryImpl.java).
The easiest way to run an existing run configuration is using ProgramRunnerUtil.executeConfiguration(RunnerAndConfigurationSettings, Executor) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/ProgramRunnerUtil.java). RunnerAndConfigurationSettings can be retrieved with, e.g., RunManager.getConfigurationSettings(ConfigurationType). The executor can be retrieved with a static method if a required executor exposes one or with ExecutorRegistry.getExecutorById() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/ExecutorRegistry.java).
To check, whether a run configuration is configured correctly and can be executed, implement the RunConfiguration.checkConfiguration(). In case the run configuration settings are incomplete, the method should throw one of the following exceptions:
RuntimeConfigurationWarning (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RuntimeConfigurationWarning.java) - in case of a problem, which doesn't affect execution
RuntimeConfigurationException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RuntimeConfigurationException.java) - in case of non-fatal error, which allows executing the configuration
RuntimeConfigurationError (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RuntimeConfigurationError.java) - in case of fatal error that makes it impossible to execute the run configuration
If the configuration contains any warnings or errors, its icon will be patched with the error indicator and the message will be displayed in the configuration settings page. In case of RuntimeConfigurationError, if a user tries to execute the configuration, the run configuration settings dialog will be presented, so that the user can fix the issues before the execution.
All the mentioned exceptions allow providing a quick fix for the reported issue. If the quick fix implementation is provided, the quick fix button will be displayed next to the error message.
If a run configuration settings editor is complex, consider implementing one of the following solutions to simplify the UI:
- the recommended approach since version 2021.1
Fragmented Settings allow for the creation of a cleaner run configuration settings editor. The fragmented editor is built of reusable fragments, which can be shared between different run configuration editors.
When a user creates a new run configuration from a template, only essential fragments are displayed at first. More advanced options are hidden and must be explicitly enabled by the user from the Modify options dropdown. It makes the editor smaller, freeing it from the clutter of unused settings fields.
To implement a fragmented settings editor in a run configuration, extend RunConfigurationFragmentedEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/ui/RunConfigurationFragmentedEditor.java) and implement createRunFragments(). The method must return a list of SettingsEditorFragment (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/execution/ui/SettingsEditorFragment.java) instances, which represent particular settings fragments that users can enable and configure.
Examples:
JavaApplicationSettingsEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/execution/impl/src/com/intellij/execution/application/JavaApplicationSettingsEditor.java)
MavenRunConfigurationSettingsEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/run/configuration/MavenRunConfigurationSettingsEditor.kt)
A complex settings editor can be split into smaller editors focused on a specific area, e.g.:
Configuration - containing the main configuration settings
Logs - containing settings related to logging
Coverage - containing settings related to code coverage
etc.
These editors should be added to the SettingsEditorGroup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/options/SettingsEditorGroup.java) object, which is a SettingsEditor's implementation itself and must be returned from getConfigurationEditor() or getRunnerSettingsEditor(). Each editor added to the group is displayed in a separate tab.
Example: ApplicationConfiguration.getConfigurationEditor() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java)
Some run configurations contain references to classes, files, or directories in their settings, and these settings usually need to be updated when the corresponding element is renamed or moved. To support that, a run configuration needs to implement the RefactoringListenerProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RefactoringListenerProvider.java) interface.
The RefactoringListenerProvider.getRefactoringElementListener()'s implementation should check whether the refactored element is referred from the run configuration. If it is, return a RefactoringElementListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/refactoring/listeners/RefactoringElementListener.java) that updates the run configuration according to the new name and location of the element.
Plugins can modify existing run configurations before they are run, e.g., by adding additional process parameters. However, there is no single platform-wide extension point, and different IDEs provide different configuration base classes and extension points, allowing for their modifications. To see what is possible in your case, check the RunConfigurationExtensionBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configuration/RunConfigurationExtensionBase.java) inheritors. Examples:
RunConfigurationExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/execution/impl/src/com/intellij/execution/RunConfigurationExtension.java) implementations registered in com.intellij.runConfigurationExtension EP allow for modifying Java run configurations extending RunConfigurationBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfigurationBase.java).
PythonRunConfigurationExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/run/PythonRunConfigurationExtension.java) implementations registered in Pythonid.runConfigurationExtension EP allow for modifying configuration extending AbstractPythonRunConfiguration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java) etc.
Run configurations can define user environment variables specific to a given run configuration and include system environment variables. Sometimes, it is convenient to reference existing variables in newly created variables, e.g., if a user creates an EXTENDED_PATH variable and builds it from a custom entry and the system PATH variable, they should reference it in the value by surrounding it with the $ character: /additional/entry:$PATH$.
To substitute variable references with the actual references, it is required to call EnvironmentUtil.inlineParentOccurrences() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/EnvironmentUtil.java) (available since 2023.2).
Sometimes, it is necessary to perform specific tasks before a configuration is actually run, e.g., build the project, run a build tool preparation task, launch a web browser, etc. Plugins can provide custom tasks that can be added by users to a created run configuration.
To provide a custom task, implement BeforeRunTaskProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/BeforeRunTaskProvider.java) and register it in com.intellij.stepsBeforeRunProvider EP. The provider implementation is responsible for creating a task instance for a given run configuration and executing the task.
If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
Macros are dynamic variables, which can be referenced in run configurations, and expanded to actual values when a run configuration is executed.
For example, a macro with a name ProjectFileDir can be referenced as $ProjectFileDir$ in a run configuration command line argument. It is expanded to the absolute path of the current project directory when the run configuration is executed by a user.
A list of built-in macros is available in the IntelliJ IDEA Web Help (https://www.jetbrains.com/help/idea/built-in-macros.html) and other products' documentation pages.
Note that Macro API is not specific to execution or run configuration API and can be used in other places.
Macro selecting support can be added to a text field on the run configuration editor by installing it with MacrosDialog.addMacroSupport() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/ide/macro/MacrosDialog.java) or other similar methods from this class. After installation, a text field will be extended by a button invoking the macro dialog, which lists available macros with descriptions and previews. After selecting and accepting a macro from the list, the macro placeholder is inserted into the text field at the current caret position.
Macros used in run configuration must be expanded to actual values before the process execution. It is usually done in the RunProfile.getState() method called during the execution workflow ("Execution Workflow" in "Execution"). To expand configured values, use one of ProgramParametersConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/util/ProgramParametersConfigurator.java) 's method: expandMacros(), expandPathAndMacros(), or expandMacrosAndParseParameters(). See their Javadocs for the details.
If the predefined list of macros is insufficient, a plugin can provide custom macros by extending Macro (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/macro/src/com/intellij/ide/macro/Macro.java) and registering it in the com.intellij.macro EP.
Product Help: Run/Debug Configuration (https://www.jetbrains.com/idea/help/run-debug-configuration.html)
This step-by-step guide shows how to register and implement a simple run configuration (Run Configurations). Run configurations are used to run internal and external processes from within IntelliJ Platform based products.
The full implementation is available in the code samples (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/run_configuration).
Create an empty plugin project. See the Creating a Plugin Gradle Project section for details.
Implement ConfigurationType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ConfigurationType.java):
Register implemented configuration type in com.intellij.configurationType extension point in the plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/run_configuration/src/main/resources/META-INF/plugin.xml):
<extensions defaultExtensionNs="com.intellij">
<configurationType
implementation="org.jetbrains.sdk.runConfiguration.DemoRunConfigurationType"/>
</extensions>Implement a new ConfigurationFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/ConfigurationFactory.java) through which custom run configurations will be created.
Implement corresponding configuration options class extending RunConfigurationOptions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfigurationOptions.kt) to store settings.
To make your changes visible from the UI, implement a new run configuration.
In most of the cases it is sufficient derive a custom run configuration class from the RunConfigurationBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfigurationBase.java). If implementing specific settings externalization rules and I/O behaviour is required, use RunConfiguration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution/src/com/intellij/execution/configurations/RunConfiguration.java) interface.
Execute ("Executing the Plugin" in "Creating a Plugin Gradle Project") the plugin.
Go to Run | Edit Configurations..., click to Add button (+ icon), and select Demo.
In the Script file field provide the path to an example script (e.g. displaying "Hello world" message).
Click the Apply button and close the dialog.
In the run toolbar select created configuration and click the run button.
The script should be executed and its result should be displayed in the console.
This page gives an overview of the Version Control Integration API.
Reference: OSS plugins providing VCS (https://jb.gg/ipe?extensions=com.intellij.vcs)
A FilePath (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/vcs/FilePath.java) represents a path to a file or directory on disk or in the VCS repository. Unlike a virtual file (Virtual Files), a FilePath can represent a path to a file which doesn't exist on disk. The main difference between a FilePath and a java.io.File (https://docs.oracle.com/javase/8/docs/api/java/io/File.html) is that a FilePath caches the VirtualFile corresponding to the path, so it can be retrieved without doing a VFS search.
To create instances of FilePath, the VcsContextFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/actions/VcsContextFactory.java) API is used.
FilePath representing paths in a VCS repository, rather than local paths, are created using VcsContextFactory.createFilePathOnNonLocal(). The FilePath.isNonLocal() method returns true for such files.
A VcsRevisionNumber (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/vcs-api-core/src/com/intellij/openapi/vcs/history/VcsRevisionNumber.java) represents a revision number of the file. If the VCS stores revision numbers as simple integers, the standard VcsRevisionNumber (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/vcs-api-core/src/com/intellij/openapi/vcs/history/VcsRevisionNumber.java) Int implementation can be used. If the VCS has a more complex format of revision numbers (like CVS, which uses a series of numbers delimited with dots), the plugin can provide a custom implementation.
A ContentRevision (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/vcs-api-core/src/com/intellij/openapi/vcs/changes/ContentRevision.java) represents a particular revision of a file, which exists either locally or in a VCS repository. It has three main attributes:
FilePath specifying the file of which this is a revision. If some version of the file exists locally, this should be a local path.
VcsRevisionNumber (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/vcs-api-core/src/com/intellij/openapi/vcs/history/VcsRevisionNumber.java) specifying the revision number of the revision, or VcsRevisionNumber.NULL if the revision exists only locally.
Content of the revision.
The content is returned as string, and the VCS plugin is responsible for converting the binary file content to correct encoding. To detect the encoding automatically based on the IDE settings and the byte order mark, the method CharsetToolkit.bytesToString() can be used (this API is new in IDEA 7.0.2). Revisions of binary files can also be represented as BinaryContentRevision (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/BinaryContentRevision.java). For binary revisions, the result of getContent() is undefined, and getBinaryContent() can be used to retrieve the contents as a byte array.
A useful class which can be used to represent the current on-disk version of a particular file is CurrentContentRevision (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/CurrentContentRevision.java).
A FileStatus (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/vcs/FileStatus.java) represents a status of a file in regard to VCS (unversioned, not changed, added, modified and so on). It determines the color used to render the name of the file in the UI.
A Change (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/vcs-api-core/src/com/intellij/openapi/vcs/changes/Change.java) represents a single file operation (creation, modification, move/rename or deletion) from a VCS point of view. A Change can represent either a modification which the user has performed locally and not yet committed, a committed modification, or some other type of modification (for example, a shelved change or a difference between two arbitrary revisions).
A Change essentially consists of two content revisions:
before revision (null if the Change represents file creation)
after revision (null if the Change represents file deletion)
A move or rename is represented by a Change where the before revision and the after revision have different file paths. A custom file status can be specified for a Change if it represents a non-standard modification of the file (for example, a file which has been merged with conflicts). If a custom file status has not been specified, the status is calculated automatically from the change type.
A ChangeList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeList.java) represents a named group of related changes. There are two main kinds of changelists:
LocalChangeList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/LocalChangeList.java) represents a group of modifications done by a user locally. If the VCS also supports the concept of changelists (like Perforce does), the VCS plugin can synchronize the IDE's local changelist structure with that of the VCS. Otherwise, a local changelist is simply a subset of the files checked out or modified by the user.
CommittedChangeList (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/versionBrowser/CommittedChangeList.java) represents a set of modifications checked in to the VCS repository. For VCSes which support atomic commit, every committed revision is represented by a CommittedChangeList. For VCSes which use per-file commit (like CVS), the plugin can use heuristics to group a sequence of individual file commits into a CommittedChangeList.
The Unversioned Files, Locally Deleted Files, etc., nodes in the Changes view are not actually change lists, and files under those nodes are not represented by ChangeList objects.
This section describes the different components which comprise a VCS integration plugin, roughly in the same order as they should be implemented.
This is the main entry point for a VCS plugin, which is used by the IntelliJ Platform to retrieve all other services provided by the plugin. Register AbstractVcs implementation in com.intellij.vcs extension point in plugin.xml (Plugin Configuration File), as shown in the following example:
<idea-plugin>
...
<extensions defaultExtensionNs="com.intellij">
<vcs name="svn" vcsClass="org.jetbrains.idea.svn.SvnVcs"/>
</extensions>
</idea-plugin>Here name is the unique name of the VCS (this must match the string returned by your implementation of AbstractVcs.getName()), and vcsClass is your implementation class.
ChangeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeProvider.java) is responsible for tracking user changes to the working copy, and reporting these changes to the IntelliJ Platform core. An implementation of this class is returned from AbstractVcs.getChangeProvider() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/AbstractVcs.java).
The ChangeProvider works in tandem with VcsDirtyScopeManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java). It keeps track of the 'dirty scope': the set of files for which the VCS file status may be out of date. Files are added to the dirty scope either when they are modified on disk, or when their VCS status is invalidated by an explicit call to VcsDirtyScopeManager.fileDirty() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java) or VcsDirtyScopeManager.dirDirtyRecursively() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManager.java).
After some files have been added to the dirty scope, the dirty scope is passed to ChangeProvider.getChanges(), along with a ChangelistBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangelistBuilder.java) instance, which serves as a sink to which the ChangeProvider feeds the data about the changed files. This processing happens asynchronously in a background thread.
The ChangeProvider can either iterate all files under the dirty scope using VcsDirtyScope.iterate() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-api/src/com/intellij/openapi/vcs/changes/VcsDirtyScope.java), or retrieve information about its contents using the getDirtyFiles() and getDirtyDirectoriesRecursively() methods. If it is possible to retrieve the information about the local changes from the VCS in batch, it's strongly preferable to use the second method, as it scales much better for large working copies.
The ChangeProvider reports data to ChangelistBuilder using the following methods:
processChange() is called for files which have been checked out (or modified if the VCS doesn't use an explicit checkout model), scheduled for addition or deletion, moved or renamed.
processUnversionedFile() is called for files which exist on disk, but are not managed by the VCS, not scheduled for addition, and not ignored through .cvsignore or a similar mechanism.
processLocallyDeletedFile() is called for files which exist in the VCS repository, but do not exist on disk and are not scheduled for deletion.
processIgnoredFile() is called for files which are not managed by the VCS but are ignored through .cvsignore or a similar mechanism.
processSwitchedFile() is called for files or directories for which the working copy corresponds to a different branch compared to the working copy of their parent directory. This can be called for the same files for which processSwitchedFile() has already been called.
To enable IntelliJ-based IDEs and plugins for National Language Support (NLS), all String instances in the code can be split into three categories:
NLS strings - strings which are shown to users in the UI: texts in dialogs, menu items, descriptions of inspections, error messages, etc.
non-NLS strings - strings which are used internally and aren't shown in UI: attributes in configuration files, keys in indices and caches, etc.
NLS-safe strings - strings which don't need to be localized but can be shown in UI: strings written by users (e.g., parts of program code, file names, URLs, etc.), names of frameworks (in some cases names of frameworks may be translated, e.g., if they consist of multiple words), etc.
By default, a string is considered as non-NLS.
There is a set of NLS-related annotations, which can be used for annotating strings in the IDE or plugin code. Annotating strings enables inspecting NLS string content correctness.
For example, if an API method parameter is annotated as an NLS string, any hardcoded string passed as a value will be reported and extracting it to a message bundle will be proposed as a quick fix. Another example is inspecting whether a given string value is properly capitalized for the usage context.
Internationalization-related inspections can be enabled in Settings | Editor | Inspections in the following groups (some inspections require enabling Java Internationalization bundled plugin):
Java | Internationalization
Java | Properties files
Properties files
Consider using the following annotations:
@Nls (https://github.com/JetBrains/java-annotations/tree/master/common/src/main/java/org/jetbrains/annotations/Nls.java) - for NLS strings. The capitalization attribute allows to specify required capitalization.
@NonNls (https://github.com/JetBrains/java-annotations/tree/master/common/src/main/java/org/jetbrains/annotations/Nls.java) - for non-NLS strings
@NlsSafe (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/base/src/com/intellij/openapi/util/NlsSafe.java) - for NLS-safe strings
NLS context annotations are semantic annotations describing the context where the annotated strings are intended to be used. For example, @InspectionMessage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/util/InspectionMessage.java) should be used for strings displayed as messages reported by inspections.
NLS context annotations must be annotated with @Nls and they can define:
capitalization requirement - via @Nls.capitalization attribute
@NlsContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/NlsContext.java) - specifies default prefix and suffix for property keys, which will be suggested by the I18nize hardcoded string literal quick fix provided by Java | Internationalization | Hardcoded strings inspection
The IntelliJ Platform provides NLS context annotations, including:
general contexts: NlsContexts (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/NlsContexts.java) nested annotations
action contexts: NlsActions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/util/NlsActions.java) nested annotations
miscellaneous contexts: @InspectionMessage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/util/InspectionMessage.java), @IntentionFamilyName (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/util/IntentionFamilyName.java), @IntentionName (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/util/IntentionName.java), @GutterName (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterName.java), @TooltipTitle (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ide/TooltipTitle.java)
To find all available annotations, search for @NlsContext usages in the intellij-community (https://github.com/JetBrains/intellij-community) source code.
If the provided set of NLS context annotations are not sufficient, create custom annotations.
It is important not to use the same string instance for both NLS and non-NLS categories. If it is required to use some class as a key in configuration files and present it in the UI, create two separate methods (getId, getDisplayName) even if they return the same value in the default locale.
To enable localization, all NLS strings must be placed in resource bundle files. It is important to avoid adding non-NLS strings to a resource bundle. At best, this will add unnecessary work for translators, but it is also quite possible that if such a string is translated, it could break some features.
All NLS strings from a module should be added to a *.properties file. A standard location of message files in JAR is /messages/*.properties. In Gradle-based plugin ("Gradle IntelliJ Plugin" in "Developing a Plugin") project sources, message files are located in $MODULE_ROOT$/src/main/resources/messages/*.properties.
A standard convention for naming message bundle properties file is *Bundle.properties.
A corresponding bundle class should be used to access the strings from the code.
The recommended approach to create a bundle class is to delegate getting messages to DynamicBundle (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/DynamicBundle.java), e.g.:
@NonNls
private const val BUNDLE = "messages.ExampleBundle"
internal object ExampleBundle {
private val INSTANCE = DynamicBundle(ExampleBundle::class.java, BUNDLE)
fun message(
key: @PropertyKey(resourceBundle = BUNDLE) String,
vararg params: Any
): @Nls String {
return INSTANCE.getMessage(key, *params)
}
fun lazyMessage(
@PropertyKey(resourceBundle = BUNDLE) key: String,
vararg params: Any
): Supplier<@Nls String> {
return INSTANCE.getLazyMessage(key, *params)
}
}final class ExampleBundle {
@NonNls
private static final String BUNDLE = "messages.ExampleBundle";
private static final DynamicBundle INSTANCE =
new DynamicBundle(ExampleBundle.class, BUNDLE);
private ExampleBundle() {}
public static @NotNull @Nls String message(
@NotNull @PropertyKey(resourceBundle = BUNDLE) String key,
Object @NotNull ... params
) {
return INSTANCE.getMessage(key, params);
}
public static Supplier<@Nls String> lazyMessage(
@NotNull @PropertyKey(resourceBundle = BUNDLE) String key,
Object @NotNull ... params
) {
return INSTANCE.getLazyMessage(key, params);
}
}Do not extend DynamicBundle in bundle classes.
Annotating message key parameter with @PropertyKey (https://github.com/JetBrains/java-annotations/tree/master/common/src/main/java/org/jetbrains/annotations/PropertyKey.java) adds the IDE support in the client code, e.g., reporting unresolved message keys.
IntelliJ IDEA provides inspections with fixes which help with moving strings to message bundles, e.g. Editor | Inspections | Java | Internationalization | Hardcoded strings for Java and Kotlin code.
It is possible to move multiple hardcoded strings to a message bundle in batch mode.
Invoke the Code | Analyse Code | Run Inspection by Name... action.
Select the Hardcoded strings inspection.
Select the inspection scope and run the inspection.
In the Problems tool window, select the items to internationalize.
Invoke the I18nize hardcoded string literal quick fix.
Provide the message bundle file and resource bundle expression.
Review the internationalized messages. It is possible to delete items and adjust their keys.
Click the OK button.
It is important to specify a prefix for the property key describing the context in which UI string is used, especially for short strings.
For example, set=Set property is hard to translate as the message usage context is unclear. It is required to find usages of such a message to properly translate it, which is a very time-consuming process. Also, other developers may reuse the same property for a different meaning of the word, and after that it will be impossible to translate it correctly.
The simplest way to specify the prefix is annotating the parameter with one of @NlsContext annotations. It will cause the IDE to generate prefix and suffix automatically when the string is moved to a message bundle.
The & symbol in message bundles is used to specify mnemonic (https://jetbrains.design/intellij/principles/mnemonics/) characters for buttons and labels. To use & in a value, escape it by a backslash (note that you also need to escape the backslash symbol):
section.title=Drag \\& DropTo wrap a long value in a message bundle, put a backslash at the end of the line, and continue the value on the next line with an indent. Starting spaces on the next line are ignored, so if a space character is required before the word in the next line, add a space before the backslash:
key=very, very long \
descriptionIn the above example, message("key") evaluates to very, very long description.
Note that the backslash at the end of a line doesn't add a line break into the resulting value. For line breaks, use \n.
They include but are not limited to:
capitalization (e.g., "usage" and "Usage")
pluralization (e.g., "child" and "children")
grammatical casing ("das Projekt" and "dem Projekt" - "the project" in German used for different grammatical cases: "the project" and "of the project" accordingly)
gender-based modification (e.g., "nuevo" and "nueva" - "new" in Spanish used for a masculine and feminine subject accordingly)
Such transformations make the incorrect assumption about:
existence of a transformation in another language (e.g., there might be no casing in some language)
rules of transformation in another language (e.g., capitalization rules may differ)
context of the transformed string (e.g., the same word might be used in different contexts)
In all above cases, it is better to put the result string into the bundle. Example:
Wrong | Correct |
|---|---|
Message bundle: Code: | Message bundle: Code: |
If an NLS string is not a simple literal but obtained as a concatenation of several values, always use MessageFormat (https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/text/MessageFormat.html) pattern to extract it to a message bundle.
For example, to internationalize "Delete Class " + className add the following property:
title.delete.class=Delete Class {0}and access it with message("title.delete.class", className) in the client code.
Patterns should be used even if the changing part of the string is the last or the first element in the concatenation, because this may change when the message is translated to a language with a different grammar.
Escaping Single Quotes in MessageFormat PatternsNote that single quotes are used for escaping in MessageFormat patterns, and to use ' character it is required to double it: ''.
For example, below are the correct sentences:
# single quote in the value below is not escaped, # because MessageFormat pattern is not used: checkbox.text.do.not.show.again=Don't show again # single quote in the value below is escaped, # because MessageFormat pattern is used (notice "{0}"): error.message.file.does.not.exist=File {0} doesn''t exist
If it is required to include a dynamically obtained string in a message, it is necessary to use message patterns as described above. However, in other cases, it is better to avoid composing NLS strings from smaller parts stored in separate properties:
the concatenation of words in the sentence incorrectly assumes that the translated sentence will have the same words in the same order and form in other languages
it is harder to properly translate messages
The following example won't be translated correctly into the languages which use word cases:
English messages:
dialog.title.add.0=Add {0}
dialog.title.edit.0=Edit {0}
dialog.title.name.0.1={0} ''{1}''
concept.library=Library
concept.dependency=DependencyPolish translations:
dialog.title.add.0=Dodaj {0}
dialog.title.add.0=Edytuj {0}
concept.library=Bibilioteka
concept.dependency=ZależnośćCorrect result:
message("dialog.title.name.0.1", message("concept.library"), "foo")
// Biblioteka 'foo'Wrong result:
message("dialog.title.add.0", message("concept.library"))
// Dodaj Biblioteka
// wrong case and capitalization (correct: "Dodaj bibliotekę")If several localized strings (non-user input) are used to concatenate the string, then the following techniques can be used (in order of preference):
Consider reworking the UI to avoid the string concatenation (consult the UX expert if your organization has any, or check IntelliJ Platform UI Guidelines (https://jetbrains.design/intellij/)).
Put string parts into different UI elements.
Remove a UI element which shows the concatenated string.
Rephrase the string.
Use a generic term: "Select " + term + " to preview" where term is "usage" or "occurrence" or "match" → "Select result to preview".
Don't use the term at all: "Preview " + term → "Preview"
The UI might still need a slight rework to be clear what will be previewed (consult the UX expert if possible).
In some cases, it might be correct to use the sentence concatenation:
guidelines.browser.tab.name={0} | IntelliJ Platform UI Guidelines
google.docs.browser.tab.name={0} - Google DocsProvide the whole string for each context and term pair:
dialog.title.add.library=Add Library
dialog.title.edit.library=Edit Library
dialog.title.name.library.0=Library ''{0}''
dialog.title.add.dependency=Add Dependency
dialog.title.edit.dependency=Edit Dependency
dialog.title.name.dependency.0=Dependency ''{0}''This approach is the least preferred:
Given X terms and Y contexts, it will result in X*Y strings. It is acceptable to have several strings for small X and Y. For more cases, the translations may become unmaintainable.
If terms are provided other by plugins via an extension point, the extension point API would require a change to provide full strings.
Sometimes it is required to change a message depending on a number, for example, to pluralize a noun or use a proper form for a verb. In that case, use ChoiceFormat patterns.
It starts with an index of the argument which will be used for choosing a variant, then the choice word follows, and the rest specifies pairs of numbers and corresponding values separated by |.
For example, for pattern:
title.selected.files=Selected {0,choice,1#File|2#Files}the expression message("title.selected.files", count) will evaluate to:
"Selected File" if count is 1 or less
"Selected Files" if count is 2 or greater
The number before # characters does not specify the exact value but start of the range. The corresponding variant will be used if the argument is greater or equal to the number in this clause and less than the number in the next clause. For example, for pattern:
label.selected.files={0,choice,0#No files are|1#One file is|2#A few files are|10#Many files are} selectedthe expression message("label.selected.files", 6) will evaluate to "A few files are selected".
If an argument is referred in one of the choice's variants, it becomes a message pattern itself, and this adds additional layer of escaping of single quotes, so to add ' character in such a variant, add: ''''. For example, below is the correct sentence:
warning.message={0,choice,1#One person doesn''t|2#{0} people don''''t} like MessageFormatIn some cases, the ordinal format can be useful. It adds an appropriate ending to numbers, so it turns into 1st, 2nd, 3rd, 10th, and so on:
parameter.cast.fix=Cast {0,number,ordinal} parameter to {1}Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Translations for IntelliJ Platform products and plugins can be provided in two ways:
Localizing IDEs is achieved by providing language packs (see available language packs (https://plugins.jetbrains.com/search?tags=Language%20Pack)). Language packs are IntelliJ Platform plugins containing translations of UI texts. Official language packs contain translations of all the UI texts used in the IDE and in plugins developed by JetBrains.
Please note that language packs aim for full IDE localization. If it is required to translate a plugin, see the section.
Language packs must define their language. The language definition is provided in the plugin.xml (Plugin Configuration File) file with com.intellij.languageBundle extension point (EP), e.g.:
<extensions defaultExtensionNs="com.intellij">
<languageBundle locale="zh-CN"/>
</extensions>The locale attribute defines the translation language on two possible levels:
region level, e.g.: zh-CN - Chinese (Simplified), zh-TW - Chinese (Taiwan)
language level, e.g., ja - Japanese
Please note that com.intellij.languageBundle EP is internal and should be used by JetBrains only.
It is important to note that there is no language chooser available in the IDE and language packs serve as the IDE "language switcher". Installing a language pack changes the IDE language to the one defined by the languageBundle EP. Only a single language pack can be installed at the same time, and restart is required for the translations to take effect.
See the translated elements list for the elements possible to translate. All the elements should be located in exactly the same paths as in original locations in their JAR files.
For example, if the original location of a message bundle is $PLUGIN_JAR$/messages/AbcBundle.properties, it must be located in $LANGUAGE_PACK_JAR$/messages/AbcBundle.properties.
It is allowed to organize them within localization directories or with file name language suffixes, but it is unnecessary as language pack can define only a single language.
In case of doubts, it is recommended to inspect the contents of existing language packs.
Please note that bundled translations are in the experimental state.
Since 2024.1, IntelliJ Platform partially supports providing translations directly bundled in the IDE or plugins. See the translated elements list for the elements possible to translate.
An IDE module or a plugin can provide multiple language translations in a single distribution, e.g., zh-CN and ja. Proper localization files will be used at runtime depending on the IDE language.
Translations for a specific language can be organized in two ways:
Language directory: /localization/$LANGUAGE_CODE$/$REGION_CODE$ ($REGION_CODE$ level is optional). Example:
Original template description:
/fileTemplates/code/JavaDoc Class.java.html
Translated template description:
/localization/zh/CN/fileTemplates/code/JavaDoc Class.java.html
Localization suffix in file name: /intentionDescriptions/QuickEditAction/description_$LANGUAGE_CODE$_$REGION_CODE$.html. Example:
Original template description:
/intentionDescriptions/QuickEditAction/description.html
Translated template description:
/intentionDescriptions/QuickEditAction/description_zh_CN.html
The proper directory layout/file name suffixes is the only thing needed for the translations to work. No additional actions like registering EPs are needed.
The following table contains the possible translated elements and information about their support in language packs and IDE/plugins.
Element | Language Pack | Bundled Translations |
|---|---|---|
Message bundles ("Message Bundles" in "Internationalization") (*.properties files) | Yes | Since 2024.1 Use DynamicBundle ("Message Bundle Class" in "Internationalization") |
Inspection descriptions ("Inspection Description" in "Code Inspections") (*.html files in /inspectionDescriptions directory) | Yes | Since 2024.1 |
Intention descriptions ("About Intention Actions" in "Intentions") (*.html files in /intentionDescriptions directory) | Yes | Since 2024.1 |
Searchable options ("buildSearchableOptions" in "Tasks") (*.xml file in /search) | Yes | 2024.2 (planned) |
File template descriptions ("Creating File Template Description" in "Providing File and Code Templates") (*.html files in the /fileTemplates directory) | Yes | 2024.2 (planned) |
Postfix template descriptions ("Postfix Template Description" in "Postfix Templates") (*.xml file in /postfixTemplates directory) | Yes | 2024.2 (planned) |
Tips of the day *.html files in tips directory | Yes | 2024.2 (planned) |
See the IntelliJ Platform UI Guidelines | Text (https://jetbrains.design/intellij/text/capitalization/) sections for good practices about writing UI texts.
Translations can be provided on three different levels:
region-specific translation
language-specific translation
default translation (English)
In addition, translations can be organized in directories or with file suffixes, and the same translation can be provided by a language pack or IDE/plugin.
All these conditions determine how a single translation is resolved at runtime. The priority is as follows:
Region level (e.g., zh_CN, zh_TW) localization file:
located within the localization directory of the language pack
located within the localization directory of the IDE or plugin
via suffix within the language pack
via suffix within the IDE or plugin
Language level (e.g., zh) localization file:
located within the localization directory of the language pack
located within the localization directory of the IDE or plugin
via suffix within the language pack
via suffix within the IDE or plugin
Default file (no suffix) within:
the language pack
the IDE or plugin
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
This topic considers the concept of projects based on the IntelliJ Platform and related subjects, such as modules, facets, libraries, and SDK. The project structure and Java classes available to manage projects and modules are discussed.
Internal changes related to a significant redesign of the representation of project models are included in 2020.3 release; please see blog post (https://blog.jetbrains.com/platform/2020/10/new-implementation-of-project-model-interfaces-in-2020-3/) for details. This shouldn't affect any plugins using the IntelliJ API properly and which don't access internal classes.
This section briefly discusses the IDEA project structure, project components, and related terms. For more information about projects and their components, refer to Project (https://www.jetbrains.com/help/idea/about-projects.html), Module (https://www.jetbrains.com/help/idea/creating-and-managing-modules.html), Library (https://www.jetbrains.com/help/idea/working-with-libraries.html), Facet (https://www.jetbrains.com/help/idea/adding-support-for-frameworks-and-technologies.html#facets) in the IntelliJ IDEA Web Help.
Use ProjectSettingsService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ProjectSettingsService.java) to open related entries in Project Structure dialog.
In the IntelliJ Platform, a project encapsulates all of a project's source code, libraries, and build instructions into a single organizational unit. Everything done using the IntelliJ Platform SDK is done within the context of a project. A project defines collections referred to as modules and libraries. Depending on the project's logical and functional requirements, you can create a single-module or a multi-module project.
A module is a discrete unit of functionality that can be run, tested, and debugged independently. Modules include such things as source code, build scripts, unit tests, deployment descriptors, etc. In a project, each module can use a specific SDK or inherit the SDK defined at the project level (see the SDK section below in this document). A module can depend on other modules of the project.
A library is an archive of compiled code (such as JAR files) on which modules depend. The IntelliJ Platform supports three types of libraries:
Module Library: the library classes are visible only in this module, and the library information is recorded in the module's .iml file.
Project Library: the library classes are visible within the project, and the library information is recorded in the project's .ipr file or in .idea/libraries.
Global Library: the library information is recorded in the applicationLibraries.xml file in the ~/.IntelliJIdea/config/options directory. Global libraries are similar to project libraries but are visible for different projects.
For more information about libraries, refer to Library (https://www.jetbrains.com/help/idea/working-with-libraries.html).
Every project uses a Software Development Kit (SDK). For Java projects, SDK is referred to as JDK (Java Development Kit).
The SDK determines which API library is used to build the project. If a project is multi-module, the project SDK is common for all modules within the project by default. Optionally, a project can configure an individual SDK for each module.
For more information about SDKs, see Working with SDKs (https://www.jetbrains.com/help/idea/working-with-sdks.html) in the IntelliJ IDEA Web Help.
A facet represents a certain configuration, specific for a particular framework/technology associated with a module. A module can have multiple facets. E.g., Spring-specific configuration is stored in a Spring facet.
Facets are documented under Facet (https://www.jetbrains.com/help/idea/adding-support-for-frameworks-and-technologies.html#facets) and Language and Framework Specific Guidelines (https://www.jetbrains.com/help/idea/language-and-framework-specific-guidelines.html) in the IntelliJ IDEA Web Help.
For more information on each of these entities, see:
Project (Project)
Module (Module)
SDK (SDK)
Library (Library)
External system integration (External System Integration) (for projects imported from Gradle or similar build systems)
Product Help: Configure projects (https://www.jetbrains.com/help/idea/working-with-projects.html)
The IntelliJ Platform stores the project configuration data in XML files. The list of those files depends on the chosen project format (https://www.jetbrains.com/help/idea/creating-and-managing-projects.html#project-formats).
For file-based format projects (legacy), the information core to the project itself (e.g., location of the component modules, compiler settings, etc.) is stored in the $project_name$.ipr file. The information about modules the project includes is stored in $module_name$.iml files. Module files are created for each module.
For directory-based format projects, the project and workspace settings are stored in a number of XML files under the $project_home_directory$/.idea directory. Each XML file is responsible for its own set of settings and can be recognized by its name: projectCodeStyle.xml, encodings.xml, vcs.xml etc. As for the file-based format projects, .iml files describe modules.
Note that direct access to project files isn't required to load or save settings. See Persisting State of Components (Persisting State of Components) for more information.
To work with projects and project files, use the following classes and interfaces:
ProjectRootManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectRootManager.java)
ProjectManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/project/ProjectManager.java)
ProjectFileIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java)
Other classes for working with the project model are located in the projectModel-api.openapi (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi) package. Basic API classes and interfaces for the concepts of Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java), Module (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/module/Module.java) and Application (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/Application.java) are placed in the core-api.openapi (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi) package.
A Project instance is available in multiple contexts:
It is also possible to retrieve projects in generic contexts:
Project from VirtualFile (Virtual Files):
ProjectLocator.guessProjectForFile(VirtualFile) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/ProjectLocator.kt) - returns any project containing a given file.
ProjectLocator.getProjectsForFile(VirtualFile) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/ProjectLocator.kt) - returns the list of projects that a given file is a part of.
List of currently opened projects: ProjectManager.getOpenProjects() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/project/ProjectManager.java)
Use the ProjectRootManager.getContentSourceRoots() method. To clarify this, consider the following code snippet:
String projectName = project.getName();
VirtualFile[] vFiles = ProjectRootManager.getInstance(project)
.getContentSourceRoots();
String sourceRootsList = Arrays.stream(vFiles)
.map(VirtualFile::getUrl)
.collect(Collectors.joining("\n"));
Messages.showInfoMessage("Source roots for the " + projectName +
" plugin:\n" + sourceRootsList, "Project Properties");Use ProjectFileIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java) to get this information:
ProjectFileIndex projectFileIndex =
ProjectRootManager.getInstance(project).getFileIndex();Use the ProjectFileIndex.getContentRootForFile() and ProjectFileIndex.getSourceRootForFile() methods. For example:
VirtualFile moduleContentRoot = ProjectRootManager.getInstance(project)
.getFileIndex().getContentRootForFile(virtualFileOrDirectory);
VirtualFile moduleSourceRoot = ProjectRootManager.getInstance(project)
.getFileIndex().getSourceRootForFile(virtualFileOrDirectory);Note that this method returns null if the file or directory does not belong to any source root of modules in the project.
The ProjectFileIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java) interface implements a number of methods you can use to check whether the specified file belongs to the project library classes or library sources:
isLibraryClassFile(): Returns true if the specified virtualFile is a compiled class file.
isInLibraryClasses(): Returns true if the specified virtualFileOrDirectory belongs to library classes.
isInLibrarySource(): Returns true if the specified virtualFileOrDirectory belongs to library sources.
Note that by default, the project modules use the project SDK. Optionally, you can configure an individual SDK for each module. See SDK (SDK) for more details.
Utility classes used for modifying the project structure can be found in the package projectModel-impl.openapi (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-impl/src/com/intellij/openapi). Its roots (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-impl/src/com/intellij/openapi/roots) subpackage contains instances and utilities intended for work with project and module source roots, including ModuleRootModificationUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java) and ProjectRootUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-impl/src/com/intellij/openapi/projectRoots/impl/ProjectRootUtil.java). Project structure changes need to be performed in write action ("Read-Write Lock" in "General Threading Rules").
Refer to the project_model (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_model/src/main/java/org/intellij/sdk/project/model/ModificationAction.java) code sample to learn how project structure modification can be implemented.
To receive notifications about changes in project structure (modules or libraries being added or removed, module dependencies being changed, and so on), use the message bus (Messaging Infrastructure) and the ProjectTopics.PROJECT_ROOTS topic:
project.getMessageBus().connect().subscribe(
ProjectTopics.PROJECT_ROOTS,
new ModuleRootListener() {
@Override
public void rootsChanged(@NotNull ModuleRootEvent event) {
// action
}
});If targeting 2019.3 or later, declarative registration (Listeners) is available as well.
The event only notifies that something has changed; if more details are needed about what changes have occurred, keep a copy of the state of the project structure model which is relevant, and to compare it with the state after the change.
Use ProjectManagerListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/project/ProjectManagerListener.java) listener (Listeners)
Product Help: Project Security (https://www.jetbrains.com/help/idea/project-security.html)
This API is available in platform releases 2021.2.4/2021.3.1 and later.
When a project is opened in the IDE for the first time, the user will be asked whether they trust the project or not. If the user chooses to preview the project in the safe mode, no potentially dangerous feature can be executed automatically or unexpectedly.
A plugin can check whether the project is trusted via the Kotlin extension method Project.isTrusted() or from Java via static method TrustedProjects.isTrusted(Project) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/impl/TrustedProjects.kt).
A project opened in the safe mode can become trusted afterward: either the user can click on the Trust Project link in the notification panel shown on top of the editor, or the state can be changed programmatically, for instance, if the user invokes a dangerous action, and that action proposes to switch to the Trusted mode.
Therefore, a plugin can subscribe to changes of the trusted state via application-level listener ("Defining Application-Level Listeners" in "Listeners") TrustStateListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/impl/TrustedProjects.kt) to switch on a feature that was disabled in the safe mode. To do it the plugin should implement TrustStateListener.onProjectTrusted(). Or better, use one of TrustedProjects.whenProjectTrusted() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/impl/TrustedProjects.kt) helper methods accepting a lambda, one for Kotlin and another for Java.
Suppose the feature can potentially execute malicious code, and it is not obvious that this code is going to be executed. In that case, this feature has to be disabled in the safe mode, and enabling it has to be protected via a confirmation ("Messages" in "Miscellaneous Swing Components").
Samples:
It is not obvious that opening a folder in the IDE can execute Gradle build script, which in turn can call a malicious code located inside the project => the Gradle import is disabled in the safe mode.
It is obvious that running or debugging the source code can execute malicious code => it is not necessary to wrap this action with a confirmation.
Working with the project wizard can be illustrated with the RedLine SmallTalk plugin (https://github.com/bulenkov/RedlineSmalltalk). See also Project Wizard Tutorial.
Additional support for specific tools and technologies is usually done via implementing some certain module type which is attached to the project. New module type should be derived from the class ModuleType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/module/ModuleType.java).
Main utilities to configure a custom project wizard can be found in the package lang-api.ide.util.projectWizard (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/util/projectWizard). These classes and interfaces serve the following purposes:
Modification of the configuration wizard view
Adding new steps to the wizard
Providing additional setting for project creation
Handling activities during project creation
Initial environment configuration
To create a new module type add an extension
<moduleType
id="MY_MODULE"
implementationClass="st.redline.smalltalk.module.MyModuleType"/>to the plugin.xml (https://github.com/bulenkov/RedlineSmalltalk/blob/master/resources/META-INF/plugin.xml). A custom module type should extend the ModuleType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/module/ModuleType.java) generic from ModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java). The following module type implementation (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleType.java) of a custom module type shows how this instance can be registered and implemented.
To set up a new module environment ModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java) class should be extended and registered as an extension point like the following snippet shows:
<extensions defaultExtensionNs="com.intellij">
<moduleBuilder
builderClass="org.jetbrains.plugins.ruby.rails.facet.versions.MyModuleBuilder"/>
</extensions>Functionality which is mandatory to implement consists of:
Setting up a root model for the new module by overriding
public abstract void setupRootModel(
ModifiableRootModel modifiableRootModel) throws ConfigurationException;Getting a module type
public abstract ModuleType getModuleType();See JavaModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/openapi/src/com/intellij/ide/util/projectWizard/JavaModuleBuilder.java) to understand better how to implement a module builder.
If your module type is based on the Java module and meant to support Java as well, extending JavaModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/openapi/src/com/intellij/ide/util/projectWizard/JavaModuleBuilder.java) is enough. No extension point needs to be registered. Refer to SmallTalk module type (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleType.java) to see how JavaModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/openapi/src/com/intellij/ide/util/projectWizard/JavaModuleBuilder.java) can be derived.
Starting with the 2022.1 release, IntelliJ-based IDEs use the refreshed project wizard and some module builder base classes return false from isAvailable() when the new wizard is enabled. If your module builder is not visible in 2022.1, make sure that your ModuleBuilder.isAvailable() returns true.
Module builder listener reacts on a new module creation, which could be done either as a part of the project creation process, or as adding a new module to the already existing project. To provide a certain behavior right after a module has been created, module builder should implement ModuleBuilderListener.moduleCreated(Module) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilderListener.java).
Examples of the tasks executed right after a module has been created may include configuring module roots, looking up for an SDK and setting it up, adding a specific facet if required, etc. For more details, please see the following SmallTalk custom module type (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleType.java) implementation.
Adding new steps to the module wizard can be done by overriding AbstractModuleBuilder.createWizardSteps(WizardContext, ModulesProvider) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/AbstractModuleBuilder.java). See an example module builder (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleBuilder.java). If this method returns a non-empty array of ModuleWizardStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleWizardStep.java) objects, new steps will be shown in their indexing order while creating a new module. The following implementation (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleWizardStep.java) for the SmallTalk project type illustrates how a custom wizard step can be created. The RsModuleWizardStep (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsModuleWizardStep.java) class is derived from ModuleWizardStep, which has two methods to be overridden:
public JComponent getComponent();defines how the step will look like
public void updateDataModel();commits data from UI into ModuleBuilder and WizardContext
Facets in IntelliJ are the way to store multiple kinds of module-specific settings, for instance to make a language support or framework available in some given module. To understand facets better from the end-user's point of view, see the Facet (Facet) documentation section.
To support the creation of your module when a project is imported from existing sources, extend ProjectStructureDetector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/ide/util/projectWizard/importSources/ProjectStructureDetector.java). To detect the files your module supports, implement ProjectStructureDetector.detectRoots().
Refer to the Smalltalk project structure detector (https://github.com/bulenkov/RedlineSmalltalk/blob/master/src/st/redline/smalltalk/module/RsProjectStructureDetector.java) to see example implementation.
But detecting the files is not enough, you also need to create a module for the project if appropriate by implementing setupProjectStructure(). Here is an example that creates a module if no other modules exist in the project structure.
@Override
public void setupProjectStructure(@NotNull Collection<DetectedProjectRoot> roots,
@NotNull ProjectDescriptor projectDescriptor,
@NotNull ProjectFromSourcesBuilder builder) {
List<ModuleDescriptor> modules = projectDescriptor.getModules();
if (modules.isEmpty()) {
modules = new ArrayList<>();
for (DetectedProjectRoot root : roots) {
modules.add(new ModuleDescriptor(root.getDirectory(),
MyModuleType.getInstance(), ContainerUtil.emptyList()));
}
projectDescriptor.setModules(modules);
}
}This set of tutorials shows how to manipulate the process of project creation. Configuring Project Wizard automatically allows you to do the following:
Adding New Steps to Project Wizard (Adding New Steps to Project Wizard)
Supporting Module Types (Supporting Module Types)
Note:
Main utilities to configure a custom project wizard can be found in the package lang-api.ide.util.projectWizard (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/util/projectWizard).
This tutorial shows how to add an extra step to the Project Wizard to provide additional project configuration settings.
Create an empty plugin project. See the Creating a Plugin Gradle Project section for details.
Project configuration settings depend on the project's module type. Register a new com.intellij.moduleBuilder extension point in the plugin.xml (Plugin Configuration File) configuration file.
<extensions defaultExtensionNs="com.intellij">
<moduleBuilder
builderClass="org.intellij.sdk.project.wizard.DemoModuleWizardStep"
id="DEMO_STEP"
order="first"/>
</extensions>Extend ModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java) class to provide custom configuration.
public class DemoModuleWizardStep extends ModuleBuilder {
public void setupRootModel(ModifiableRootModel modifiableRootModel)
throws ConfigurationException {
}
}Set a module type for the extra wizard step to provide. In this example, choose an EMPTY module type.
public class DemoModuleWizardStep extends ModuleBuilder {
public void setupRootModel(ModifiableRootModel modifiableRootModel)
throws ConfigurationException {
}
public ModuleType getModuleType() {
return ModuleType.EMPTY; //or it could be any other module type
}
}Provide an implementation of a custom UI component to be added to the Wizard. In this case, leave it as a label.
public class DemoModuleWizardStep extends ModuleBuilder {
public void setupRootModel(ModifiableRootModel modifiableRootModel)
throws ConfigurationException {
}
public ModuleType getModuleType() {
return ModuleType.EMPTY;
}
@Override
public ModuleWizardStep[] createWizardSteps(
@NotNull WizardContext wizardContext,
@NotNull ModulesProvider modulesProvider) {
return new ModuleWizardStep[]{new ModuleWizardStep() {
@Override
public JComponent getComponent() {
return new JLabel("Put your content here");
}
@Override
public void updateDataModel() {
}
}};
}
}After compiling and running the plugin, create a new project using a source-compiled instance of IntelliJ IDEA.

Choose an Empty Module type, click next, and get to the just added extra step.

Modify and tune the UI component depending on requirements.
IntelliJ Platform provides a set of standard module types. However, an application might need a module of a type that isn't supported yet. This tutorial shows how to register a new module type and link it to the project creation procedure and the UI.
The source code for the module (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/module) and project_wizard (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_wizard) code samples is used throughout this tutorial.
Create an empty plugin project. See the Creating a Plugin Gradle Project section for details.
The UI for selecting module types and the creation of modules through project wizard is IntelliJ IDEA-specific.
Add a new com.intellij.moduleType implementation with the IntelliJ Platform in the plugin.xml (Plugin Configuration File) configuration file.
<extensions defaultExtensionNs="com.intellij">
<moduleType
id="DEMO_MODULE_TYPE"
implementationClass="org.intellij.sdk.module.DemoModuleType"/>
</extensions>Create the DemoModuleType implementation based on ModuleType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/module/ModuleType.java).
getNodeIcon() should return module type specific icon.
Create DemoModuleBuilder based on ModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java).
Provide a straightforward implementation of UI components for the project creating stage. Create a generic DemoModuleWizardStep based on ModuleWizardStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleWizardStep.java)
After compiling and running the plugin in a development instance, create a new project. Select File | New | Module.... A new module type and its settings panel are available in the Project Wizard.

The following tutorial shows how to support a custom framework type for a project and make this framework type embedded in a project wizard (Project Wizard - Adding Support for Creating New Project Types) as a UI component. The examples in this tutorial rely heavily on the framework_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/framework_basics) code sample.
Note that this feature requires a dependency (Plugin Dependencies) on the Java plugin ("Java" in "IntelliJ IDEA Plugin Development").
In oder to make a custom framework available and configurable for a project the FrameworkTypeEx (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/framework/FrameworkTypeEx.java) class needs to be extended, in this example to make the DemoFramework (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/framework_basics/src/main/java/org/intellij/sdk/framework/DemoFramework.java) class.
final class DemoFramework extends FrameworkTypeEx {
}The newly created framework class should be registered as an extension point by adding com.intellij.framework.type extension in plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/framework_basics/src/main/resources/META-INF/plugin.xml) configuration file:
<extensions defaultExtensionNs="com.intellij">
<framework.type
implementation="org.intellij.sdk.framework.DemoFramework"/>
</extensions>The framework component should have a unique name passed as a string literal to the constructor. It is best if this is the FQN name of the class:
final class DemoFramework extends FrameworkTypeEx {
public static final String FRAMEWORK_ID =
"org.intellij.sdk.framework.DemoFramework";
DemoFramework() {
super(FRAMEWORK_ID);
}
}The Presentable name and icon define the appearance of visual components related to the framework:
final class DemoFramework extends FrameworkTypeEx {
@NotNull
@Override
public String getPresentableName() {
return "SDK Demo Framework";
}
@NotNull
@Override
public Icon getIcon() {
return SdkIcons.Sdk_default_icon;
}
}To make the framework set up available while executing the steps to create a project, the DemoFramework.createProvider() method must be implemented to return an object of type FrameworkSupportInModuleConfigurable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/framework/addSupport/FrameworkSupportInModuleConfigurable.java), which adds the framework to a module. In this example the framework is added to any ModuleType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/module/ModuleType.java) without checking, which is usually not the case.
@NotNull
@Override
public FrameworkSupportInModuleProvider createProvider() {
return new FrameworkSupportInModuleProvider() {
@NotNull
@Override
public FrameworkTypeEx getFrameworkType() {
return DemoFramework.this;
}
@NotNull
@Override
public FrameworkSupportInModuleConfigurable createConfigurable(
@NotNull FrameworkSupportModel model) {
return new FrameworkSupportInModuleConfigurable() {
@Override
public JComponent createComponent() {
return new JCheckBox("SDK Extra Option");
}
@Override
public void addSupport(@NotNull Module module,
@NotNull ModifiableRootModel model,
@NotNull ModifiableModelsProvider provider) {
// This is the place to set up a library, generate a specific file,
// and actually add framework support to a module.
}
};
}
@Override
public boolean isEnabledForModuleType(@NotNull ModuleType type) {
return true;
}
};
}See Code Samples (Code Samples) on how to set up and run the plugin.
Extra option for configuring the newly created Demo custom framework should be available in the Project Wizard:

Product Help: Modules (https://www.jetbrains.com/help/idea/modules.html)
A module is a discrete unit of functionality that can be run, tested, and debugged independently. Modules include such things as source code, build scripts, unit tests, deployment descriptors, etc.
The key components of a module are:
Content roots - the directories where the files belonging to the module (source code, resources, etc.) are stored. Each directory can belong to one and only one module; it's not possible to share a content root between multiple modules.
Source roots - A content root can have multiple source roots underneath it. Source roots can have different types: regular source roots, test source roots, resource roots, etc. In IntelliJ IDEA, source roots are used as roots of the package hierarchy structure. Java classes directly under a source root will be in the root package. Source roots can also be used to implement more fine-grained dependency checks. Code under a regular source root cannot depend on code under a test source root.
Not all other IntelliJ Platform-based IDEs use source roots.
Order entries - the dependencies of a module, which are stored in an ordered list. A dependency can be a reference to an SDK (SDK), a library (Library), or another module.
Facets (Facet) - the list of framework-specific configuration entries.
In addition to that, a module can store other settings, such as a module-specific SDK (SDK), compile output path settings, etc. Plugins can store additional data associated with a module by creating facets or module-level components.
The IntelliJ Platform provides a number of classes and interfaces you can use to work with modules:
ModuleUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/openapi/module/ModuleUtil.java) and ModuleUtilCore (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/module/ModuleUtilCore.java)
ModuleRootManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootManager.java)
ModuleRootModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootModel.java)
ModifiableModuleModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/module/ModifiableModuleModel.java)
ModifiableRootModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModifiableRootModel.java)
This section discusses how to complete some common tasks related to management of modules.
See Changing the Project Structure ("Changing the Project Structure" in "Project") for information on modifying project/module structure.
Use the ModuleManager.getModules() method.
Order entries include SDK, libraries and other modules the module uses. With the IntelliJ IDEA UI, you can view order entries for a module on the Dependencies tab of the Project Structure dialog box.
To explore the module dependencies, use the OrderEnumerator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/OrderEnumerator.java) class.
The following code snippet illustrates how you can get classpath (classes root of all dependencies) for a module:
VirtualFile[] roots = ModuleRootManager.getInstance(module).orderEntries().classes().getRoots();Use the ModuleRootManager.getSdk() method. This method returns a value of the Sdk (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/projectRoots/Sdk.java) type.
The following code snippet illustrates how you can get detailed information on SDK the specified module uses:
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Sdk sdk = moduleRootManager.getSdk();
String jdkInfo = "Module: " + module.getName() +
" SDK: " + sdk.getName() +
" SDK version: " + sdk.getVersionString() +
" SDK home directory: " + sdk.getHomePath();Use the ModuleRootManager.getDependencies() method to get an array of the Module type values or the ModuleRootManager.getDependencyModuleNames() to get an array of module names. To clarify, consider the following code snippet:
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
Module[] dependentModules = moduleRootManager.getDependencies();
String[] dependentModulesNames = moduleRootManager.getDependencyModuleNames();Use the ModuleManager.getModuleDependentModules(module) method.
Note that you can also check whether a module (module1) depends on another specified module (module2) using the ModuleManager.isModuleDependent() method in the following way:
boolean isDependent = ModuleManager.getInstance(project).isModuleDependent(module1, module2);To get the project module to which the specified file belongs, use the ModuleUtil.findModuleForFile() static method.
To clarify, consider the following code snippet:
String pathToFile = "/Users/john/plugins/myPlugin/src/MyAction.java";
VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(pathToFile);
Module module = ModuleUtil.findModuleForFile(virtualFile, myProject);
String moduleName = module == null ? "Module not found" : module.getName();To get the project module to which the specified PSI element (PSI Elements) belongs, use the ModuleUtil.findModuleForPsiElement() method.
Use ModulePointer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/module/ModulePointer.java) to store a reference to a Module by its instance or name. A removal or rename of the Module will be tracked automatically.
Information about module roots can be accessed via ModuleRootManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootManager.java). For example, the following snippet shows how to access the content roots of a module:
VirtualFile[] contentRoots = ModuleRootManager.getInstance(module).getContentRoots();To check if a virtual file or directory belongs to a module source root, use the ProjectFileIndex.getSourceRootForFile() method. This method returns null if the file or directory does not belong to any source root of modules in the project.
VirtualFile moduleSourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFileOrDirectory);Obtain CompilerModuleExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/CompilerModuleExtension.java) for given Module instance to access Compiler Output path related properties.
To receive notifications about module changes (modules being added, removed or renamed), use the message bus (Messaging Infrastructure) and the ProjectTopics.MODULES topic:
project.getMessageBus().connect().subscribe(
ProjectTopics.MODULES,
new ModuleListener() {
@Override
public void moduleAdded(@NotNull Project project, @NotNull Module module) {
// action
}
});If targeting 2019.3 or later, declarative registration (Listeners) is available as well.
Product Help: SDKs (https://www.jetbrains.com/help/idea/working-with-sdks.html)
Every project uses a Software Development Kit (SDK). For Java projects, the SDK is referred to as the JDK (Java Development Kit). The SDK determines which API library is used to build the project. If a project is multi-module, the project SDK by default is common for all modules within the project. Optionally, individual SDKs for each module can be configured.
The information about the project SDK is accessed via ProjectRootManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectRootManager.java) like the following example shows
Sdk projectSdk = ProjectRootManager.getInstance(project).getProjectSdk();To get the project-level SDK:
Sdk projectSdk = ProjectRootManager.getInstance(project).getProjectSdkName();To get the project-level SDK name:
String projectSdkName = ProjectRootManager.getInstance(project).getProjectSdkName();To set the project-level SDK:
ProjectRootManager.getInstance(project).setProjectSdk(sdk);To set the project-level SDK name:
ProjectRootManager.getInstance(project).setProjectSdkName(name, sdk.getSdkType().getName());See the project_model (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_model/src/main/java/org/intellij/sdk/project/model/ProjectSdkAction.java) code sample to get more familiar with SDK manipulation toolset.
ProjectJdkTable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/projectRoots/ProjectJdkTable.java) can be used to query and modify configured SDKs.
To create a custom SDK, provide a class extending SdkType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/projectRoots/SdkType.java), leave saveAdditionalData() blank, and register it in the com.intellij.sdkType extension point.
To make SDK settings persistent, override setupSdkPaths() and save settings by modificator.commitChanges():
@Override
public boolean setupSdkPaths(@NotNull Sdk sdk, @NotNull SdkModel sdkModel) {
SdkModificator modificator = sdk.getSdkModificator();
modificator.setVersionString(getVersionString(sdk));
modificator.commitChanges(); // save
return true;
}To let a user select an SDK, see ProjectJdksEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/openapi/projectRoots/ui/ProjectJdksEditor.java).
However, it is not recommended to use "SDK" in non-IntelliJ IDEA IDEs. Although "SDK" is available in most JetBrains products, ProjectJdksEditor is specific to Java, making the operation around "SDK" difficult. The recommended way of managing "SDK" settings is to create a CustomStepProjectGenerator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/util/projectWizard/CustomStepProjectGenerator.java) implementation and save settings in a PersistentStateComponent (Persisting State of Components).
Prompting the user with a notification to set up an SDK can help them get up-and-running with a plugin faster. Use com.intellij.projectSdkSetupValidator extension point to register an implementation of ProjectSdkSetupValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/daemon/ProjectSdkSetupValidator.java) to notify the user if they are missing an SDK.
The following is a simplified example that checks whether an instance of "DemoSdk" has been configured in the project when the user opens a "DemoFileType":
internal class DemoProjectSdkSetupValidator : ProjectSdkSetupValidator {
override fun isApplicableFor(project: Project, file: VirtualFile): Boolean {
return file.fileType == DemoFileType
}
override fun getErrorMessage(project: Project, file: VirtualFile): String? {
if (ProjectJdkTable.getInstance().getSdksOfType(DemoSdkType.getInstance()).isEmpty()) {
return "No DemoSdks are configured for this project!"
}
return null
}
override fun getFixHandler(project: Project, file: VirtualFile):
EditorNotificationPanel.ActionHandler {
return SdkPopupFactory.newBuilder()
.withProject(project)
.withSdkTypeFilter { it is DemoSdkType }
.updateSdkForFile(file)
.buildEditorNotificationPanelHandler()
}
}Within DemoProjectSdkSetupValidator:
isApplicableFor() checks what condition(s) should be met to run the validation.
getErrorMessage() runs the validation and return an appropriate error message if the validation fails.
If the validation is successful, then it should return null.
getFixHandler() returns an EditorNotificationPanel.ActionHandler that enables the user to execute a quick fix to resolve the validation issue.
ProjectSdkSetupValidator will not work in IntelliJ Platform-based IDEs such as PyCharm. In such cases, you should register an implementation of EditorNotifications.Provider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/EditorNotifications.java) at the com.intellij.editorNotificationProvider extension point and override the createNotificationPanel() method with the conditionality and panel setup you want.
Product Help: Libraries (https://www.jetbrains.com/help/idea/library.html)
A library is an archive of compiled code (such as JAR files) that modules depend on.
The IntelliJ Platform supports three types of libraries:
Module Library: the library classes are visible only in this module and the library information is recorded in the module .iml file.
Project Library: the library classes are visible within the project and the library information is recorded under .idea/libraries directory or in the project .ipr file.
Global Library: the library information is recorded in the applicationLibraries.xml file in $USER_HOME$/.IntelliJIdea/config/options directory. Global libraries are similar to project libraries, but are visible for different projects.
A particular type of programmatically defined libraries is Predefined Libraries.
Package com.intellij.openapi.roots.libraries (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/libraries) provides functionality for working with project libraries and JAR files.
To get the list of libraries that a module depends on, use OrderEnumerator.forEachLibrary as follows.
List<String> libraryNames = new ArrayList<>();
ModuleRootManager.getInstance(module).orderEntries().forEachLibrary(library -> {
libraryNames.add(library.getName());
return true;
});
Messages.showInfoMessage(StringUtil.join(libraryNames, "\n"), "Libraries in Module");This sample code outputs a list of libraries that the given module depends on.
To manage the lists of application and project libraries, use LibraryTable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTable.java). The list of application-level library tables is accessed by calling LibraryTablesRegistrar.getLibraryTable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/LibraryTablesRegistrar.java), whereas the list of project-level library tables is accessed via LibraryTablesRegistrar.getLibraryTable(Project). Once you have a LibraryTable, you can get the libraries in it by calling LibraryTable.getLibraries().
To get the list of all module libraries defined in a given module, use API from OrderEntryUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/OrderEntryUtil.java):
OrderEntryUtil.getModuleLibraries(ModuleRootManager.getInstance(module));Library (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/libraries/Library.java) provides the getUrls() method you can use to get a list of source roots and classes the library includes. To clarify, consider the following code snippet:
StringBuilder roots = new StringBuilder("The " + lib.getName() + " library includes:\n");
roots.append("Sources:\n");
for (String each : lib.getUrls(OrderRootType.SOURCES)) {
roots.append(each).append("\n");
}
roots.append("Classes:\n");
for (String each : lib.getUrls(OrderRootType.CLASSES)) {
roots.append(each).append("\n");
}
Messages.showInfoMessage(roots.toString(), "Library Info");To create a library, perform the following steps:
Get a write action ("Read-Write Lock" in "General Threading Rules")
Obtain the library table to which you want to add the library. Use one of the following, depending on the library level:
LibraryTablesRegistrar.getInstance().getLibraryTable()
LibraryTablesRegistrar.getInstance().getLibraryTable(Project)
ModuleRootManager.getInstance(module).getModifiableModel().getModuleLibraryTable()
Create the library by calling LibraryTable.createLibrary()
Add contents to the library (see below)
For a module-level library, commit the modifiable model returned by ModuleRootManager.getInstance(module).getModifiableModel().
For module-level libraries, you can also use simplified APIs in the ModuleRootModificationUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ModuleRootModificationUtil.java) class to add a library with a single API call. You can find an example of using these APIs in the project_model (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_model/src/main/java/org/intellij/sdk/project/model/ModificationAction.java) code sample.
To add or change the roots of a library, you need to perform the following steps:
Get a write action ("Read-Write Lock" in "General Threading Rules")
Get a modifiable model for the library, using Library.getModifiableModel()
Use methods such as Library.ModifiableModel.addRoot() to perform the necessary changes
Commit the model using Library.ModifiableModel.commit().
Use ModuleRootModificationUtil.addDependency(Module, Library) from under a write action ("Read-Write Lock" in "General Threading Rules").
The ProjectFileIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java) interface implements a number of methods you can use to check whether the specified file belongs to the project library classes or library sources. You can use the following methods:
To check if a specified virtual file is a compiled class file use
ProjectFileIndex.isLibraryClassFile(virtualFile)To check if a specified virtual file or directory belongs to library classes use
ProjectFileIndex.isInLibraryClasses(virtualFileorDirectory)To check if the specified virtual file or directory belongs to library sources use
ProjectFileIndex.isInLibrarySource(virtualFileorDirectory)See the project_model (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_model/src/main/java/org/intellij/sdk/project/model/ProjectFileIndexSampleAction.java) to see how the method mentioned above can be applied.
More details on libraries can be found in the plugin_model (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/project_model/src/main/java/org/intellij/sdk/project/model/LibrariesAction.java) code sample.
EP: com.intellij.additionalLibraryRootsProvider
AdditionalLibraryRootsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/AdditionalLibraryRootsProvider.java) Allows providing synthetic/predefined libraries (SyntheticLibrary (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/SyntheticLibrary.java)) in a project without exposing them in the model. By default, they're also hidden from UI.
Product Help: Facets (https://www.jetbrains.com/help/idea/facet-page.html), Adding frameworks (facet) (https://www.jetbrains.com/help/idea/adding-support-for-frameworks-and-technologies.html)
A facet represents configuration specific for a particular framework/technology, associated with a module. A module can have multiple facets. E.g. Spring Framework specific configuration is stored in a Spring facet.
Please see Facet Basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/facet_basics) sample plugin project.
To create, search and access the list of facets for a module use FacetManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/facet/FacetManager.java).
A tool window (Tool Windows) dependent on the existence of given facet(s) can be registered via com.intellij.facet.toolWindow extension point.
This page provides a high-level overview of the External System sub-system. There are multiple project management systems (Apache Maven (https://maven.apache.org/), Gradle (https://www.gradle.org/), sbt (https://www.scala-sbt.org/), etc.) and IntelliJ Platform provides a mechanism to support them in IDEs.
Most of the project management systems provide a similar set of facilities from the integration point of view:
build a project from external system config (pom.xml, build.gradle.kts, etc.)
provide a list of available tasks
allow to execute a particular task
and more
That means that we can separate external system-specific logic and general IDE processing. The External System sub-system provides a simple API for wrapping external system elements and extensible IDE-specific processing logic.
The external system wrapper is required to be able to build project info on the basis of the given external system config. That information is built with the following base classes:
ExternalEntityData (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ExternalEntityData.java)
The DataNode class is just a holder for the target data (a data type is defined by the Key). Multiple DataNode objects might be organized in directed graph where every edge identifies parent-child relation.
For example, a simple one-module project might look as below:
The IDE provides a set of built-in Key and ExternalEntityData classes but any external system integration or third-party plugin developer might enhance project data by defining custom Key and ExternalEntityData and store them at a child of appropriate DataNode.
Processing project data built on an external system config basis can be performed with ProjectDataService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/manage/ProjectDataService.java). It is a strategy which knows how to manage particular ExternalEntityData. For example, when we want to import a project from an external model, we can start with the top level DataNode which references project info and then import its data using corresponding service.
Custom services can be registered via com.intellij.externalProjectDataService (https://jb.gg/ipe?extensions=com.intellij.externalProjectDataService) extension point.
The good thing is that we can separate project parsing and management here. That means that a set of DataNode, Key and ProjectDataServices can be introduced for particular technology and then every external system integration can build corresponding data if necessary using it.
The IntelliJ Platform provides an API for importing projects from external models:
ProjectImportBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/projectImport/ProjectImportBuilder.java)
ProjectImportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/projectImport/ProjectImportProvider.java)
There are two classes built on the template method pattern which simplify implementation:
AbstractExternalProjectImportBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/openapi/externalSystem/service/project/wizard/AbstractExternalProjectImportBuilder.java)
AbstractExternalProjectImportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/openapi/externalSystem/service/project/wizard/AbstractExternalProjectImportProvider.java)
Note that AbstractExternalProjectImportBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/openapi/externalSystem/service/project/wizard/AbstractExternalProjectImportBuilder.java) is built on top of the 'external system settings' controls.
Concrete implementations should be registered in com.intellij.projectImportBuilder (https://jb.gg/ipe?extensions=com.intellij.projectImportBuilder) and com.intellij.projectImportProvider (https://jb.gg/ipe?extensions=com.intellij.projectImportProvider) extension points accordingly.
Example of the project import provider and builder for Gradle:
JavaGradleProjectImportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/src/service/project/wizard/JavaGradleProjectImportProvider.kt)
JavaGradleProjectImportBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/src/service/project/wizard/JavaGradleProjectImportBuilder.kt)
It's possible to configure external system integration to automatically refresh project structure when external project's config files are modified.
From 2020.1, auto-import cannot be disabled by a user.
Describe project's settings files to track by having external system ExternalSystemManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemManager.java) implement ExternalSystemAutoImportAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/src/com/intellij/openapi/externalSystem/ExternalSystemAutoImportAware.java).
The ExternalSystemAutoImportAware.getAffectedExternalProjectPath() method is called quite often, that's why it's expected to return control as soon as possible. Helper CachingExternalSystemAutoImportAware class might be used for caching, i.e. ExternalSystemManager which implements ExternalSystemAutoImportAware can have a field like new CachingExternalSystemAutoImportAware(new MyExternalSystemAutoImportAware()) and delegate ExternalSystemAutoImportAware.getAffectedExternalProjectPath() calls to it.
Some external systems don't have ExternalSystemManager (e.g., Maven), but they also can use auto-import core to track changes in settings files. For this, implement ExternalSystemProjectAware interface that describes settings files for tracking and an action to reload the project model. Then register the instance with ExternalSystemProjectTracker to start tracking.
Multiple ExternalSystemProjectAware instances can correspond to a single external system. It allows performing project reload differently depending on the set of settings files (project aware per settings file, per module, per external project, etc.).
From 2020.1, the icon for reload notification can be specified per external system. Implement ExternalSystemIconProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/src/com/intellij/openapi/externalSystem/ui/ExternalSystemIconProvider.kt) and register via com.intellij.externalIconProvider (https://jb.gg/ipe?extensions=com.intellij.externalIconProvider) extension point in plugin.xml (Plugin Configuration File). Alternatively, set reloadIcon field external system implements ExternalSystemIconProvider directly.
All external system settings controls are represented by implementations of ExternalSystemSettingsControl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemSettingsControl.java). There are general and linked project-level external system settings. A particular external system settings UI contains the following items:
General system settings
Linked external projects list
Project-level settings for the selected project
It's recommended to extend from AbstractExternalProjectSettingsControl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java) for implementing project-level settings control as it already handles some of them.
Examples:
GradleSystemSettingsControl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleSystemSettingsControl.java) handling the General settings in Settings | Build, Execution, Deployment | Build Tools | Gradle
GradleProjectSettingsControl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/src/org/jetbrains/plugins/gradle/service/settings/GradleProjectSettingsControl.java) handling the selected Gradle project settings in Settings | Build, Execution, Deployment | Build Tools | Gradle
A similar approach is used for providing settings in importing external project UI. Implementation is expected to extend AbstractImportFromExternalSystemControl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/src/com/intellij/openapi/externalSystem/service/settings/AbstractImportFromExternalSystemControl.java) and instead of linked external projects list it contains target external project path control.
2022.1
Use com.jetbrains.intellij.platform:external-system-test-framework from IntelliJ Platform Artifacts Repositories.
Relevant base classes:
ExternalSystemImportingTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/testFramework/src/com/intellij/platform/externalSystem/testFramework/ExternalSystemImportingTestCase.java)
ExternalSystemTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/testFramework/src/com/intellij/platform/externalSystem/testFramework/ExternalSystemTestCase.java)
The Program Structure Interface, commonly referred to as just PSI, is the layer in the IntelliJ Platform responsible for parsing files and creating the syntactic and semantic code model that powers so many of the platform's features.
PSI Files (PSI Files)
File View Providers (File View Providers)
PSI Elements (PSI Elements)
See useful tools ("3.1 Use Internal Mode and PsiViewer" in "Explore the IntelliJ Platform API") on how to inspect the PSI structure and its properties.
A PSI (Program Structure Interface) file is the root of a structure representing a file's contents as a hierarchy of elements in a particular programming language.
The PsiFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFile.java) class is the common base class for all PSI files, while files in a specific language are usually represented by its subclasses. For example, the PsiJavaFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiJavaFile.java) class represents a Java file, and the XmlFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlFile.java) class represents an XML file.
Unlike Virtual Files and Documents, which have application scope (even if multiple projects are open, each file is represented by the same VirtualFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/VirtualFile.java) instance), PSI has Project scope: the same file is represented by multiple PsiFile instances if the file belongs to multiple projects open at the same time.
Most interesting modification operations are performed on the level of individual PSI elements, not files as a whole.
To iterate over the elements in a file, use
psiFile.accept(new PsiRecursiveElementWalkingVisitor() {
// visitor implementation ...
});See also Navigating the PSI.
As PSI is language-dependent, PSI files are created using the Language (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Language.java) instance:
LanguageParserDefinitions.INSTANCE
.forLanguage(MyLanguage.INSTANCE)
.createFile(fileViewProvider);Like Documents, PSI files are created on-demand when the PSI is accessed for a particular file.
Like Documents, PSI files are weakly referenced from the corresponding VirtualFile instances and can be garbage-collected if not referenced by anyone.
PsiFileFactory.createFileFromText() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFileFactory.java) creates an in-memory PSI file with the specified contents.
To save the PSI file to disk, use its parent directory's PsiDirectory.add() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiDirectory.java).
PsiManager.addPsiTreeChangeListener() allows you to receive notifications about all changes to the PSI tree of a project. Alternatively, register PsiTreeChangeListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiTreeChangeListener.java) in com.intellij.psi.treeChangeListener extension point.
Please see PsiTreeChangeEvent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiTreeChangeEvent.java) Javadoc for common problems when dealing with PSI events.
PSI can be extended to support additional languages through custom language plugins. For more details on developing custom language plugins, see the Custom Language Support reference guide.
Any changes done to the content of PSI files are reflected in documents, so all rules for working with documents ("What are the rules of working with documents?" in "Documents") (read/write actions, commands, read-only status handling) are in effect.
A file view provider (FileViewProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/FileViewProvider.java)) manages access to multiple PSI trees within a single file.
For example, a JSPX page has a separate PSI tree for the Java code in it (PsiJavaFile), a separate tree for the XML code (XmlFile), and a separate tree for JSP as a whole (JspFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/jsp-openapi/src/com/intellij/psi/jsp/JspFile.java)).
Each of the PSI trees covers the entire contents of the file and contains special "outer language elements" in the places where contents in a different language can be found.
A FileViewProvider instance corresponds to a single VirtualFile, a single Document, and can retrieve multiple PsiFile instances.
Context | API |
|---|---|
PSI File (PSI Files) | PsiFile.getViewProvider() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFile.java) |
Virtual File (Virtual Files) | PsiManager.getInstance(project).findViewProvider() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiManager.java) |
To get the set of all languages for which PSI trees exist in a file: fileViewProvider.getLanguages()
To get the PSI tree for a particular language: fileViewProvider.getPsi(language). For example, to get the PSI tree for XML, use fileViewProvider.getPsi(XMLLanguage.INSTANCE).
To find an element of a particular language at the specified offset in the file: fileViewProvider.findElementAt(offset, language)
To create a file type that has multiple interspersing trees for different languages, a plugin must contain an extension to the com.intellij.fileType.fileViewProviderFactory extension point.
Implement FileViewProviderFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/FileViewProviderFactory.java) and return your FileViewProvider implementation from createFileViewProvider() method.
Register as follows in plugin.xml (Plugin Configuration File):
<extensions defaultExtensionNs="com.intellij">
<fileType.fileViewProviderFactory
filetype="$FILE_TYPE$"
implementationClass="com.example.MyFileViewProviderFactory"/>
</extensions>Where $FILE_TYPE$ refers to the type of the file being created (for example, "JSF").
A PSI (Program Structure Interface) file represents a hierarchy of PSI elements (so-called PSI trees). A single PSI file (PSI Files) (itself being a PSI element) may expose several PSI trees in specific programming languages (see File View Providers). A PSI element, in its turn, can have child PSI elements.
PSI elements and operations at the level of individual PSI elements are used to explore the source code's internal structure as it is interpreted by the IntelliJ Platform. For example, you can use PSI elements to perform code analysis, such as code inspections (https://www.jetbrains.com/help/idea/code-inspection.html) or intention actions (https://www.jetbrains.com/idea/help/intention-actions.html).
The PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) class is the common base class for PSI elements.
See PSI Cookbook and Modifying the PSI.
There are three main ways to navigate the PSI: top-down, bottom-up, and references. In the first scenario, you have a PSI file or another higher-level element (for example, a method). You need to find all elements that match a specified condition (for example, all variable declarations). In the second scenario, you have a specific point in the PSI tree (for example, the element at caret) and need to find out something about its context (for example, the element in which it has been declared). Finally, references allow you to navigate from the usages of an element (e.g., a method call) to the declaration (the method being called) and back. References are described in a separate topic (PSI References).
The most common way to perform top-down navigation is to use a Visitor (https://en.wikipedia.org/wiki/Visitor_pattern). To use a visitor, you create a class (usually an anonymous inner class) that extends the base visitor class, overrides the methods that handle the elements you're interested in, and passes the visitor instance to PsiElement.accept().
The base classes for visitors are language-specific. For example, if you need to process elements in a Java file, you extend JavaRecursiveElementVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/JavaRecursiveElementVisitor.java) and override the methods corresponding to the Java element types you're interested in.
The following snippet shows the use of a visitor to find all Java local variable declarations:
file.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitLocalVariable(@NotNull PsiLocalVariable variable) {
super.visitLocalVariable(variable);
System.out.println("Found a variable at offset " +
variable.getTextRange().getStartOffset());
}
});In many cases, you can also use more specific APIs for top-down navigation. For example, if you need to get a list of all methods in a Java class, you can use a visitor, but a much easier way to do that is calling PsiClass.getMethods().
PsiTreeUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java) contains a number of general-purpose, language-independent functions for PSI tree navigation, some of which (for example, findChildrenOfType()) perform top-down navigation.
The starting point for bottom-up navigation is either a specific element in the PSI tree (for example, the result of resolving a reference) or an offset. If you have an offset, you can find the corresponding PSI element by calling PsiFile.findElementAt(). This method returns the element at the lowest level of the tree (for example, an identifier), and you need to navigate the tree up if you want to determine the broader context.
In most cases, bottom-up navigation is performed by calling PsiTreeUtil.getParentOfType(). This method goes up the tree until it finds the element of the type you've specified. For example, to find the containing method, you call PsiTreeUtil.getParentOfType(element, PsiMethod.class).
In some cases, you can also use specific navigation methods. For example, to find the class where a method is contained, you call PsiMethod.getContainingClass().
The following snippet shows how these calls can be used together:
PsiFile psiFile = anActionEvent.getData(CommonDataKeys.PSI_FILE);
PsiElement element = psiFile.findElementAt(offset);
PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
PsiClass containingClass = containingMethod.getContainingClass();To see how the navigation works in practice, please refer to the code sample (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/psi_demo/src/main/java/org/intellij/sdk/psi/PsiNavigationDemoAction.java).
A reference in a PSI tree is an object that represents a link from a usage of a particular element in the code to the corresponding declaration. Resolving a reference means locating the declaration to which a specific usage refers.
The most common type of reference is defined by language semantics. For example, consider a simple Java method:
public void hello(String message) {
System.out.println(message);
}This simple code fragment contains five references. The references created by the identifiers String, System, out, and println can be resolved to the corresponding declarations in the JDK: the String and System classes, the out field, and the println method. The reference created by the second occurrence of the message identifier in println(message) can be resolved to the message parameter, declared by String message in the method header.
Note that String message is not a reference and cannot be resolved. Instead, it's a declaration. It does not refer to any name defined elsewhere; instead, it defines a name by itself.
A reference is an instance of a class implementing the PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) interface. Note that references are distinct from PSI elements. References created by a PSI element are returned from PsiElement.getReferences(), the underlying PSI element of a reference can be obtained from PsiReference.getElement().
To resolve the reference - to locate the declaration being referenced - call PsiReference.resolve(). It's very important to understand the difference between PsiReference.getElement() and PsiReference.resolve(). The former method returns the source of a reference, while the latter returns its target. In the example above, for the message reference, getElement() will return the message identifier on the second line of the snippet, and resolve() will return the message identifier on the first line (inside the parameter list).
The process of resolving references is distinct from parsing and is not performed at the same time. Moreover, it is not always successful. If the code currently open in the IDE does not compile, or in other situations, it's normal for PsiReference.resolve() to return null - all code working with references must be prepared to handle that.
Please see also "Cache Results of Heavy Computations" in "PSI Performance".
In addition to references defined by the semantics of the programming language, the IDE recognizes many references determined by the semantics of the APIs and frameworks used in code. Consider the following example:
File file = new File("foo.txt");Here, "foo.txt" has no special meaning from the point of view of the Java syntax - it's just a string literal. However, opening this example in IntelliJ IDEA and having a file called "foo.txt" in the same directory, one can Ctrl/CmdClick on "foo.txt" and navigate to the file. This works because the IDE recognizes the semantics of new File(...) and contributes a reference into the string literal passed as a parameter to the method.
Typically, references can be contributed to elements that don't have their own references, such as string literals and comments. References are also often contributed to non-code files, such as XML or JSON.
Contributing references is one of the most common ways to extend an existing language. For example, your plugin can contribute references to Java code, even though the Java PSI is part of the platform and not defined in your plugin.
Implement PsiReferenceContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java) registered in com.intellij.psi.referenceContributor extension point.
Attribute language should be set to the Language ID where this contributor applies to. The exact places to contribute references to are then specified using Element Patterns in calls to PsiReferenceRegistrar.registerReferenceProvider().
See also Reference Contributor tutorial (10. Reference Contributor).
In the simplest case, a reference resolves to a single element, and if resolving fails, the code is incorrect, and the IDE needs to highlight it as an error. However, there are cases when the situation is different.
The first case is soft references. Consider the new File("foo.txt") example above. If the IDE can't find the file "foo.txt", it doesn't mean that an error needs to be highlighted - maybe the file is only available at runtime. Such references return true from the PsiReference.isSoft() method, which can then be used in inspection/annotator to skip highlighting them completely or use a lower severity.
The second case is polyvariant references. Consider the case of a JavaScript program. JavaScript is a dynamically typed language, so the IDE cannot always precisely determine which method is being called at a particular location. To handle this, it provides a reference that can be resolved to multiple possible elements. Such references implement the PsiPolyVariantReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiPolyVariantReference.java) interface.
For resolving a PsiPolyVariantReference, you call its multiResolve() method. The call returns an array of ResolveResult (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/ResolveResult.java) objects. Each of the objects identifies a PSI element and also specifies whether the result is valid. For example, suppose you have multiple Java method overloads and a call with arguments not matching any of the overloads. In that case, you will get back ResolveResult objects for all the overloads, and isValidResult() returns false for all of them.
Resolving a reference means going from usage to the corresponding declaration. To perform the navigation in the opposite direction - from a declaration to its usages - perform a references search.
To perform a search using ReferencesSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/searches/ReferencesSearch.java), specify the element to search for, and optionally other parameters such as the scope in which the reference needs to be searched. The created Query (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/Query.java) allows obtaining all results at once or iterating over the results one by one. The latter allows stopping processing as soon as the first (matching) result has been found.
Please refer to the guide (References and Resolve) and corresponding tutorial (10. Reference Contributor) for more information.
The PSI is a read/write representation of the source code as a tree of elements corresponding to a source file's structure. You can modify the PSI by adding, replacing, and deleting PSI elements.
To perform these operations, you use methods such as PsiElement.add(), PsiElement.delete(), and PsiElement.replace(), as well as other methods defined in the PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) interface that let you process multiple elements in a single operation, or to specify the exact location in the tree where an element needs to be added.
Like document operations, PSI modifications need to be wrapped in a write action and in command (and can only be performed in the event dispatch thread). See the Documents article ("What are the rules of working with documents?" in "Documents") for more information on commands and write actions.
The PSI elements to add to the tree or replace existing PSI elements are usually created from text. In the most general case, you use the createFileFromText() method of PsiFileFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFileFactory.java) to create a new file that contains the code construct which you need to add to the tree or to use as a replacement for an existing element, traverse the resulting tree to locate the specific part that you need, and then pass that element to add() or replace(). See also "How do I create a PSI file?" in "PSI Files".
Most languages provide factory methods that let you create specific code constructs more easily. Examples:
PsiJavaParserFacade (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiJavaParserFacade.java) class contains methods such as createMethodFromText(), which creates a Java method from the given text
SimpleElementFactory.createProperty() (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleElementFactory.java) creating a Simple language property
When you're implementing refactorings, intentions (Intentions), or inspection quickfixes (Code Inspections and Intentions) that work with existing code, the text that you pass to the various createFromText() methods will combine hard-coded fragments and fragments of code taken from the existing file. For small code fragments (individual identifiers), you can simply append the text from the existing code to the text of the code fragment you are building. In that case, you need to make sure that the resulting text is syntactically correct. Otherwise, the createFromText() method will throw an exception.
For larger code fragments, it's best to perform the modification in several steps:
create a replacement tree fragment from the text, leaving placeholders for the user code fragments;
replace the placeholders with the user code fragments;
replace the element in the original source file with the replacement tree.
This ensures that the user code's formatting is preserved and that the modification does not introduce any unwanted whitespace changes. Just as everywhere else in the IntelliJ Platform API, the text passed to createFileFromText() and other createFromText() methods must use only \n as line separators.
As an example of this approach, see the quickfix in the ComparingStringReferencesInspection example (Code Inspections):
The PSI modification methods do not restrict you in the way you can build the resulting tree structure. For example, when working with a Java class, you can add a for statement as a direct child of a PsiMethod element, even though the Java parser will never produce such a structure (the for statement will always be a child of the PsiCodeBlock) representing the method body. Modifications that produce incorrect tree structures may appear to work, but they will lead to problems and exceptions later. Therefore, you always need to ensure that the structure you built with PSI modification operations is the same as what the parser would produce when parsing the code that you've created.
To make sure you're not introducing inconsistencies, you can call PsiTestUtil.checkFileStructure() in the tests for your action that modifies the PSI. This method ensures that the structure you've built is the same as what the parser produces.
When working with PSI modification functions, you should never create individual whitespace nodes (spaces or line breaks) from the text. Instead, all whitespace modifications are performed by the formatter, which follows the code style settings selected by the user. Formatting is automatically performed at the end of every command, and if you need, you can also perform it manually using the reformat(PsiElement) method in the CodeStyleManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java) class.
Also, when working with Java code (or with code in other languages with a similar import mechanism such as Groovy or Python), you should never create imports manually. Instead, you should insert fully-qualified names into the code you're generating, and then call the shortenClassReferences() method in the JavaCodeStyleManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/codeStyle/JavaCodeStyleManager.java) (or the equivalent API for the language you're working with). This ensures that the imports are created according to the user's code style settings and inserted into the file's correct place.
In some cases, you need to perform a PSI modification and then to perform an operation on the document you've just modified through the PSI (for example, start a live template (Live Templates)). To complete the PSI-based post-processing (such as formatting) and commit the changes to the document, call doPostponedOperationsAndUnblockDocument() on PsiDocumentManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiDocumentManager.java) instance.
This page gives recipes for the most common operations for working with the PSI (Program Structure Interface).
Unlike Developing Custom Language Plugins (Custom Language Support), it is about working with the PSI of existing languages (such as Java).
See also the PSI Performance section.
FilenameIndex.getFilesByName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/FilenameIndex.java)
ReferencesSearch.search() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/searches/ReferencesSearch.java)
RefactoringFactory.createRename() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/refactoring/RefactoringFactory.java)
FileContentUtil.reparseFiles() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/util/FileContentUtil.java)
If your plugin depends on Java functionality and targets 2019.2 or later, see "Java" in "Plugin Compatibility with IntelliJ Platform Products". Also consider using UAST (UAST - Unified Abstract Syntax Tree) if your plugin supports other JVM languages.
ClassInheritorsSearch.search() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java)
JavaPsiFacade.findClass() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/JavaPsiFacade.java)
PsiShortNamesCache.getClassesByName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-api/src/com/intellij/psi/search/PsiShortNamesCache.java)
PsiClass.getSuperClass() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiClass.java)
PsiJavaFile javaFile = (PsiJavaFile) psiClass.getContainingFile();
PsiPackage psiPackage = JavaPsiFacade.getInstance(project)
.findPackage(javaFile.getPackageName());or
PsiUtil.getPackageName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java)
OverridingMethodsSearch.search() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-api/src/com/intellij/psi/search/searches/OverridingMethodsSearch.java)
2023.2
Use dedicated (and heavily cached) methods from JavaLibraryUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/openapi/src/com/intellij/java/library/JavaLibraryUtil.java):
hasLibraryClass() to check presence via known library class FQN
hasLibraryJar() using Maven coordinates (for example, io.micronaut:micronaut-core).
See also "Avoiding UI Freezes" in "General Threading Rules" and "Improving Indexing Performance" in "Indexing and PSI Stubs".
IDE Perf (https://plugins.jetbrains.com/plugin/15104-ide-perf) plugin provides on-the-fly performance diagnostic tools, including a dedicated view for CachedValue metrics.
Avoid PsiElement's methods which are expensive with deep trees.
getText() traverses the whole tree under the given element and concatenates strings, consider using textMatches() instead.
getTextRange(), getContainingFile(), and getProject() traverse the tree up to the file, which can be long in very nested trees. If you only need PSI element length, use getTextLength().
getContainingFile() and getProject() often can be computed once per task and then stored in fields or passed via parameters.
Additionally, methods such as getText(), getNode(), or getTextRange(), need the AST, obtaining which can be quite an expensive operation, see next section.
Avoid loading too many parsed trees or documents into memory at the same time. Ideally, only AST nodes from files open in the editor should be present in the memory. Everything else, even if it's needed for resolve/highlighting purposes, can be accessed via PSI interfaces, but its implementations should use stubs (Stub Indexes) underneath, which are less CPU- and memory-expensive.
If stubs don't suit your case well (e.g., the information you need is large and/or very rarely needed, or you're developing a plugin for a language whose PSI you don't control), you can create a custom index or gist (Indexing and PSI Stubs).
To ensure you're not loading AST accidentally, you can use AstLoadingFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/AstLoadingFilter.java) in production and PsiManagerEx.setAssertOnFileLoadingFilter() in tests.
The same applies to documents: only the ones opened in editors should be loaded. Usually, you shouldn't need document contents (as most information can be retrieved from PSI). If you nevertheless need documents, consider saving the information you need to provide in a custom index or gist (Indexing and PSI Stubs) to get it more cheaply later. If you still need documents, then at least ensure you load them one by one and don't hold them on strong references to let GC free the memory as quickly as possible.
Method calls such as PsiElement.getReference() (and getReferences()), PsiReference.resolve() (and multiResolve() and other equivalents) or computation of expression types, type inference results, control flow graphs, etc. can be expensive. To avoid paying this cost several times, the result of such computation can be cached and reused. Usually, CachedValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/CachedValue.java) created with CachedValueManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java) works well for this purpose.
If the information you cache depends only on a subtree of the current PSI element (and nothing else: no resolve results or other files), you can cache it in a field in your PsiElement implementation and drop the cache in an override of ASTDelegatePsiElement.subtreeChanged().
Since 2024.1, the platform no longer increments root changes modification tracker on finish of dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"). If cached values use ProjectRootManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectRootManager.java) as dependency (without PsiModificationTracker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/PsiModificationTracker.java)) and at the same time depend on indexes (Indexing and PSI Stubs), a dependency on DumbService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbService.kt) must be added.
The indexing framework provides a quick way to locate specific elements, e.g., files containing a certain word or methods with a particular name, in large codebases. Plugin developers can use the existing indexes built by the IDE itself and build and use their own indexes.
It supports two main types of indexes:
File-based indexes are built directly over the content of files. Stub indexes are built over serialized stub trees. A stub tree for a source file is a subset of its PSI tree, which contains only externally visible declarations and is serialized in a compact binary format.
Querying a file-based index gets you the set of files matching a specific condition. Querying a stub index gets you the set of matching PSI elements. Therefore, custom language plugin developers typically use stub indexes in their plugin implementations.
Index Viewer (https://plugins.jetbrains.com/plugin/13029-index-viewer/) plugin can be used to inspect indexes' contents and properties.
Indexing is a potentially lengthy process. It's performed in the background, and during this time, all IDE features are restricted to the ones that don't require indexes: basic text editing, version control, etc. This restriction is managed by DumbService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbService.kt). Violations are reported via IndexNotReadyException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/IndexNotReadyException.java), see its documentation for information on how to adapt callers.
DumbService provides API to query whether the IDE is currently in "dumb" mode (where index access is not allowed) or "smart" mode (with all index built and ready to use). It also provides ways of delaying code execution until indexes are ready.
Implementations of certain Extension Points can be marked as available during Dumb Mode by implementing DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java). Such Extension Points are marked with DumbAware tag in IntelliJ Platform Extension Point and Listener List. Commonly used include CompletionContributor (Code Completion), (External)Annotator ("Annotator" in "Syntax and Error Highlighting") and various run configuration (Run Configurations) EPs.
For Actions available during Dumb Mode, extend DumbAwareAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/project/DumbAwareAction.java).
Other API might indicate its Dumb Mode compatibility by extending PossiblyDumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/PossiblyDumbAware.java).
To toggle Dumb Mode for testing purposes, invoke Tools | Internal Actions | Enter/Exit Dumb Mode while the IDE is running in internal mode (Enabling Internal Mode).
Sometimes, the following conditions hold:
The aggregation functionality of file-based indexes is not needed. One just needs to calculate some data based on a particular file's contents and cache it on disk.
Eagerly calculating the data for the entire project during indexing isn't needed (e.g., it slows down the indexing, and/or this data probably will ever be required for a minor subset of all project files).
The data can be recalculated lazily on request without significant performance penalties.
A file-based index (File-Based Indexes) can be used in such cases, but file gists provide a way to perform data calculation lazily, caching on disk, and a more lightweight API. Please see VirtualFileGist (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/gist/VirtualFileGist.java) and PsiFileGist (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/gist/PsiFileGist.java) documentation.
Note performance implications noted in VirtualFileGist (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/gist/VirtualFileGist.java) Javadoc.
Example:
VirtualFileGist: ImageInfoIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/images/src/org/intellij/images/index/ImageInfoIndex.java) calculating image dimensions/bit depth needed to be displayed in specific parts of UI.
PsiFileGist: JavaSimplePropertyGist (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-impl/src/com/intellij/psi/impl/JavaSimplePropertyGist.kt) providing simple properties in Java
Indexing performance metrics in JSON format are generated in logs directory (https://intellij-support.jetbrains.com/hc/en-us/articles/206544519-Directories-used-by-the-IDE-to-store-settings-caches-plugins-and-logs) (see sandbox directory ("The Development Instance Sandbox Directory" in "IDE Development Instance") for development instance) in 2020.2 and later. These are additionally available in HTML format starting with 2021.1.
Use lexer (Implementing Lexer) information instead of parsed trees if possible.
If impossible, use light AST which doesn't create memory-hungry AST nodes inside, so traversing it might be faster. Obtain LighterAST (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/LighterAST.java) by casting FileContent input parameter to PsiDependentFileContent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/indexing/PsiDependentFileContent.java) and calling getLighterAST(). Make sure to traverse only the nodes you need to. See also LighterASTNodeVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/psi/impl/source/tree/LighterASTNodeVisitor.java) and LightTreeUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/psi/impl/source/tree/LightTreeUtil.java) for useful utility methods.
For stub index (Stub Indexes), implement LightStubBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/psi/stubs/LightStubBuilder.java).
If a custom language contains lazy-parseable elements that never or rarely contain any stubs, consider implementing StubBuilder.skipChildProcessingWhenBuildingStubs() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/StubBuilder.java) (preferably using Lexer/node text).
For indexing XML, also consider using NanoXmlUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-impl/src/com/intellij/util/xml/NanoXmlUtil.java).
For bigger projects, building and providing pre-built shared project indexes can be beneficial, see Shared project indexes (https://www.jetbrains.com/help/idea/shared-indexes.html#project-shared-indexes). See also IntelliJ Shared Indexes Tool Example (https://github.com/JetBrains/intellij-shared-indexes-tool-example).
File-based indexes are based on a Map/Reduce architecture (https://en.wikipedia.org/wiki/MapReduce). Each index has a specific type of key and a particular type of value.
The key is what's later used to retrieve data from the index.
Example: in the word index, the key is the word itself.
The value is arbitrary data, which is associated with the key in the index.
Example: in the word index, the value is a mask indicating in which context the word occurs (code, string literal, or comment).
In the simplest case, when one needs to know in what files some data is present, the value has type Void and is not stored in the index.
When the index implementation indexes a file, it receives a file's content and returns a map from the keys found in the file to the associated values.
When accessing an index, specify the key you're interested in and get back the list of files in which the key occurs, and the value associated with each file.
In some cases, using Gists ("Gists" in "Indexing and PSI Stubs") can be considered as an alternative.
A relatively simple file-based index implementation is the UI Designer bound forms index (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer/src/com/intellij/uiDesigner/binding/FormClassIndex.java), storing FQN of bound implementation class for GUI Designer (https://www.jetbrains.com/help/idea/gui-designer-basics.html) .form files.
Each specific index implementation is a class extending FileBasedIndexExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/FileBasedIndexExtension.java) registered via com.intellij.fileBasedIndex extension point.
An implementation of a file-based index consists of the following main parts:
getIndexer() returns the DataIndexer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/indexing/DataIndexer.java) implementation actually responsible for building a set of key/value pairs based on file content.
getKeyDescriptor() returns the KeyDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/io/KeyDescriptor.java) responsible for comparing keys and storing them in a serialized binary format. Probably the most commonly used implementation is EnumeratorStringDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/io/EnumeratorStringDescriptor.java), which is designed for storing identifiers efficiently.
getValueExternalizer() returns the DataExternalizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/io/DataExternalizer.java) responsible for storing values in a serialized binary format.
getInputFilter() allows restricting the indexing only to a certain set of files. Consider using DefaultFileTypeSpecificInputFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/DefaultFileTypeSpecificInputFilter.java).
getName() returns a unique index ID. Consider using fully qualified index class name to not clash with other plugins defining index with the same ID, e.g., com.example.myplugin.indexing.MyIndex.
getVersion() returns the version of the index implementation. The index is automatically rebuilt if the current version differs from the version of the index implementation used to build it.
If there's no value to associate with the files (i.e., value type is Void), simplify the implementation by extending ScalarIndexExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/ScalarIndexExtension.java). In case of single value per file, extend from SingleEntryFileBasedIndexExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/SingleEntryFileBasedIndexExtension.java).
Please see also Improving indexing performance ("Improving Indexing Performance" in "Indexing and PSI Stubs").
Critical Implementation Notes
Value class must implement equals() and hashCode() properly, so a value deserialized from binary data should be equal to original one.
The data returned by DataIndexer.map() must depend only on input data passed to the method, and must not depend on any external files. Otherwise, your index will not be correctly updated when the external data changes, and you will have stale data in your index.
Please set system property intellij.idea.indices.debug/intellij.idea.indices.debug.extra.sanity to true to enable additional debugging assertions during development to assert correct index implementation.
Access to file-based indexes is performed through the FileBasedIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/FileBasedIndex.java) class.
Please note index access is restricted during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs").
The following primary operations are supported:
getAllKeys() and processAllKeys() allow obtaining the list of all keys found in files, which are a part of the specified project. To optimize performance, consider returning true from FileBasedIndexExtension.traceKeyHashToVirtualFileMapping() (see its Javadoc for details).
The returned data is guaranteed to contain all keys found in up-to-date project content, but may also include additional keys not currently found in the project.
getValues() allows to get all values associated with a specific key but not the files in which they were found.
getContainingFiles() allows collecting all files in which a particular key was encountered.
processValues() allows iterating through all files in which a specific key was encountered and accessing the associated values simultaneously.
When accessing index data in nested calls (usually from multiple indexes), limitations might apply.
Nested index access is now possible.
NOTE: Please do not use yet This is known to cause problems under certain conditions, please watch this issue (https://youtrack.jetbrains.com/issue/IJPL-265/Nested-index-lookups-still-leads-to-deadlocks).
Nested index access is forbidden as it might lead to a deadlock. Collect all necessary data from index A first, then process results while accessing index B.
The IntelliJ Platform contains several standard file-based indexes. The most useful indexes for plugin developers are:
Generally, the word index should be accessed indirectly by using helper methods of the PsiSearchHelper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/PsiSearchHelper.java) class.
FilenameIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/FilenameIndex.java) provides a quick way to find all files matching a specific file name.
FileTypeIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/FileTypeIndex.java) serves a similar goal: it allows to find all files of a particular FileType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/FileType.java) quickly.
To add additional files/directories to be indexed, implement IndexableSetContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/util/indexing/IndexableSetContributor.java) and register in com.intellij.indexedRootsProvider (https://jb.gg/ipe?extensions=com.intellij.indexedRootsProvider) extension point.
A stub tree is a subset of the PSI tree for a file; it is stored in a compact serialized binary format. The PSI tree for a file can be backed either by the AST (built by parsing the file) or by the stub tree deserialized from disk. Switching between the two is transparent.
The stub tree contains only a subset of the nodes. Typically, it contains only the nodes needed to resolve the declarations contained in this file from external files. Trying to access any node that is not part of the stub tree or perform any operation that cannot be satisfied by the stub tree, e.g., accessing the text of a PSI element, causes file parsing to switch from the stub to AST backing.
Each stub in the stub tree is simply a bean class with no behavior. A stub stores a subset of the corresponding PSI element's state, like the element's name, modifier flags like public or final, etc. The stub also holds a pointer to its parent in the tree and a list of its children's stubs.
To support stubs for a custom language, first decide which of the PSI tree elements should be stored as stubs. Typically, stubs are needed for things like methods or fields visible from other files. Usually there is no need to have stubs for things like statements or local variables, which are not visible externally.
When using Grammar-Kit (https://github.com/JetBrains/Grammar-Kit) to generate the language PSI, see the Stub indexes support (https://github.com/JetBrains/Grammar-Kit/blob/master/HOWTO.md#35-stub-indices-support) section for instructions on integrating the grammar with stubs.
The following steps need to be performed only once for each language that supports stubs:
Change the file element type for the language (the element type returned from ParserDefinition.getFileNodeType()) to a class that extends IStubFileElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/psi/tree/IStubFileElementType.java) and override its getExternalId() method (see also following item).
In the plugin.xml (Plugin Configuration File), define the com.intellij.stubElementTypeHolder extension and specify the interface which contains the IElementType constants used by the language's parser.
Define the common externalIdPrefix to be used for all stub element types (see ). See StubElementTypeHolderEP (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/StubElementTypeHolderEP.java) docs for important requirements.
Examples:
JavaStubElementTypes (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaStubElementTypes.java) registered in JavaPsiPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-impl/src/META-INF/JavaPsiPlugin.xml)
see Angular2MetadataElementTypes (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/Angular/src/org/angular2/entities/metadata/Angular2MetadataElementTypes.kt) for Kotlin sample
For each element type that needs to be stored in the stub tree, perform the following steps:
Define an interface for the stub, derived from the StubElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/StubElement.java) interface (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/psi/PropertyStub.java)).
Provide an implementation for the interface (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertyStubImpl.java)).
Make sure the interface for the PSI element extends StubBasedPsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/StubBasedPsiElement.java) parameterized by the type of the stub interface (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/psi/Property.java)).
Make sure the implementation class for the PSI element extends StubBasedPsiElementBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/extapi/psi/StubBasedPsiElementBase.java) parameterized by the type of the stub interface (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertyImpl.java)). Provide both a constructor that accepts an ASTNode and a constructor that accepts a stub.
Create a class that implements IStubElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/IStubElementType.java) and is parameterized with the stub interface and the actual PSI element interface (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/parsing/PropertyStubElementType.java)). Implement the createPsi() and createStub() methods for creating PSI from a stub and vice versa. Implement the serialize() and deserialize() methods for storing the data in a binary stream.
Override getExternalId() according to common used externalIdPrefix for the language (see ).
For always-leaf stub nodes return true from isAlwaysLeaf() (2023.3). "Container" stubs that do not serialize any data of their own may implement EmptyStubSerializer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/EmptyStubSerializer.java) to optimize storage (2023.3).
Use the class implementing IStubElementType as the element type constant when parsing (example (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/parsing/PropertiesElementTypes.java)).
Make sure all methods in the PSI element interface access the stub data rather than the PSI tree when appropriate (example: Property.getKey() implementation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertyImpl.java)).
By default, if a PSI element extends StubBasedPsiElement, all elements of that type will be stored in the stub tree. To have more precise control over which elements are stored, override IStubElementType.shouldCreateStub() and return false for elements that should not be included in the stub tree. The exclusion is not recursive: if some elements of the element returning false are also stub-based PSI elements, they will be included in the stub tree.
For serializing string data in stubs, e.g. element names, use StubOutputStream (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/StubOutputStream.java) writeName() and readName(). These methods ensure that each unique identifier is stored only once in the data stream. This reduces the size of the serialized stub tree data. See also DataInputOutputUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/io/DataInputOutputUtil.java).
To change the stored binary format for the stubs (for example, to store some additional data or some new elements), make sure to advance the stub version returned from IStubFileElementType.getStubVersion() for the language. This will cause the stubs and to be rebuilt, and will avoid mismatches between the stored data format and the code trying to load it.
It is critical to ensure that all information stored in the stub tree depends only on the contents of the file for which stubs are being built, and does not depend on any external files or any other data. Otherwise, the stub tree will not be rebuilt when external dependencies change, leading to stale and incorrect data in the stub tree.
Please see also "Improving Indexing Performance" in "Indexing and PSI Stubs".
When building the stub tree, the plugin can, at the same time, put some data about the stub elements into a number of indexes, which then can be used to find the PSI elements by the corresponding key. Unlike file-based indexes, stub indexes do not support storing custom data as values; the value is always a PSI element. Keys in stub indexes are typically strings (such as class names); other data types are also supported if desired.
A stub index is a class which extends AbstractStubIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/stubs/AbstractStubIndex.java). In the most common case, when the key type is String, use a more specific base class, namely StringStubIndexExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/stubs/StringStubIndexExtension.java). Stub index implementation classes are registered in the com.intellij.stubIndex extension point.
To put data into an index, implement IStubElementType.indexStub() (example: JavaClassElementType.indexStub() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassElementType.java)). This method accepts an IndexSink (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/stubs/IndexSink.java) as a parameter and puts in the index ID and the key for each index in which the element should be stored.
To access the data from an index, the following instance methods are used on the singleton instance managed by the implementation:
AbstractStubIndex.getAllKeys()/processAllKeys() returns the list of all keys (processes all keys) in the index for the specified project (for example, the list of all class names found in the project).
NOTE: These may return stale/out-of-date data. See to obtain/verify actual existing elements for the given key (e.g., when iterating all keys to collect completion variants).
StubIndex.getElements() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/stubs/StubIndex.java) returns the collection of PSI elements corresponding to a certain key (for example, classes with the specified short name) in the specified scope.
Example: JavaAnnotationIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-impl/src/com/intellij/psi/impl/java/stubs/index/JavaAnnotationIndex.java)
Element patterns provide a generic way to specify conditions on objects.
Plugin authors use them to check whether PSI elements match a particular structure. Just as regular expressions for strings test whether a (sub-)string matches a particular pattern, element patterns are used to put conditions on the nested structure of PSI elements. Their two main applications inside the IntelliJ Platform are:
Specifying where auto-completion should occur when implementing a completion contributor (9. Completion Contributor) for a custom language.
Specifying PSI elements that provide further references via a PSI reference contributor ("Contributed References" in "PSI References").
However, plugin authors rarely implement the ElementPattern (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/patterns/ElementPattern.java) interface directly. Instead, we recommend using the high-level pattern classes provided by the IntelliJ Platform:
Some built-in languages in the IntelliJ Platform implement their own pattern classes and can provide additional examples:
XmlPatterns (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/patterns/XmlPatterns.java) provides patterns for XML attributes, values, entities, and texts.
PsiJavaPatterns (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/patterns/PsiJavaPatterns.java) provides patterns for literals, strings, arguments, and function/method arguments for Java.
DomPatterns (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/patterns/DomPatterns.java) builds upon XmlPatterns and acts as a wrapper to provide further patterns for DOM-API (XML DOM API).
A good starting point for element patterns is the Custom Language Support Tutorial (Custom Language Support Tutorial). They are used in the completion ("Define a Completion Contributor" in "9. Completion Contributor") and reference ("Define a Reference Contributor" in "10. Reference Contributor") contributor section of the tutorial. However, the IntelliJ Platform source code provides many more examples of element patterns for built-in languages like JSON, XML, Groovy, Markdown, and so on. Checking the references in the table above or searching for usages of the high-level pattern classes will provide a comprehensive list that shows how element patterns are used in production code.
For instance, an example can be found in the JavaFX plugin FxmlReferencesContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/FxmlReferencesContributor.java) that tests if the given PSI element is an XML attribute value inside a *.fxml file.
XmlAttributeValuePattern attributeValueInFxml =
XmlPatterns.xmlAttributeValue().inVirtualFile(
virtualFile().withExtension(JavaFxFileTypeFactory.FXML_EXTENSION)
);As shown in the code above, element patterns can be stacked and combined to create more complex conditions. JsonCompletionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/json/src/com/intellij/json/codeinsight/JsonCompletionContributor.java) contains another example with more requirements on the PSI element.
PsiElementPattern.Capture<PsiElement> AFTER_COMMA_OR_BRACKET_IN_ARRAY =
psiElement()
.afterLeaf("[", ",")
.withSuperParent(2, JsonArray.class)
.andNot(
psiElement().withParent(JsonStringLiteral.class)
);The above pattern makes sure that the PSI element:
Appears after either an open bracket or a comma, which is expressed by putting a restriction on the neighboring leaf element.
Has JsonArray as a level-two parent, which indicates that the PSI element must be inside a JSON array.
Does not have a JsonStringLiteral as a parent, which prevents situations where a string with a bracket or comma inside an array would give a false-positive match.
This last example shows that corner cases need to be considered carefully even for simple patterns.
Working with element patterns can be tricky, and plugin authors need a solid understanding of the underlying PSI structure to get it right. Therefore, it is recommended to use the PsiViewer plugin or built-in PSI viewer ("3.1 Use Internal Mode and PsiViewer" in "Explore the IntelliJ Platform API") and verify that elements indeed have the expected structure and properties.
For this section, it is assumed that plugin authors have a basic understanding of how to work with a debugger (https://www.jetbrains.com/help/idea/debugging-code.html), how to set breakpoints (https://www.jetbrains.com/help/idea/using-breakpoints.html#set-breakpoints), and how to set conditions on breakpoints (https://www.jetbrains.com/help/idea/using-breakpoints.html#properties).
When debugging element patterns, plugin authors need to keep in mind that the places where element patterns are instantiated are unrelated to where they are actually used. For instance, while patterns for completion contributors are instantiated when registering the contributor, the patterns are checked during completion while typing. Therefore, finding the correct locations in the IntelliJ Platform for debugging element patterns is the first important step.
However, setting breakpoints inside ElementPattern will result in many false-positives since element patterns are used extensively throughout the IDE. One way to filter out these false-positives is to use a condition on the breakpoints. The following steps can help you investigate where patterns are checked:
Set breakpoints at the ElementPattern.accepts() methods.
Set a condition on the breakpoints that checks whether the string representation of the pattern contains an identifiable part of the pattern.
Debug, and when the breakpoint triggers, make sure it is the right pattern and investigate the call stack to find relevant methods that use the pattern check.
Debug the relevant methods, e.g. methods that fill completion variants or find references.
Note that finding an identifiable part of a pattern can be achieved by setting a breakpoint where the pattern is instantiated and checking its string representation.
Using the Markdown code example from above, we note that the MarkdownLinkDestinationImpl class is used in the element pattern. Now, set a breakpoint at:
com.intellij.patterns.ElementPattern#accepts(
java.lang.Object,
com.intellij.util.ProcessingContext
)Right-click on the breakpoint and set the following as a condition:
toString().contains("MarkdownLinkDestinationImpl")Now start a debug session and open a Markdown file. When the breakpoint hits, the call stack in the debug tool window (https://www.jetbrains.com/help/idea/debug-tool-window.html) shows that reference-providers are checked in the method doGetReferencesFromProviders within ReferenceProvidersRegistryImpl (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java). This provides a good starting point for further investigation.
UAST (Unified Abstract Syntax Tree) is an abstraction layer on the PSI (PSI Elements) of different programming languages targeting the JVM (Java Virtual Machine). It provides a unified API for working with common language elements like classes and method declarations, literal values, and control flow operators.
Different JVM languages have their own PSI (PSI Elements), but many IDE features like inspections, gutter markers, reference injection, and many others work the same way for all these languages. Using UAST allows providing features that will work across all supported JVM languages using a single implementation.
Presentation Writing IntelliJ Plugins for Kotlin (https://www.youtube.com/watch?v=j2tvi4GbOr4) offers a thorough overview of using UAST in real-world scenarios.
For plugins, that should work for all JVM languages in the same way.
Some known examples are:
Spring Framework (Spring API)
Android Studio (Android Studio Plugin Development)
Plugin DevKit (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/devkit/devkit-core)
Java: full support
Kotlin: full support
Scala: beta, but full support
Groovy: declarations only, method bodies not supported
UAST is a read-only API. There are experimental UastCodeGenerationPlugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/generate/UastCodeGenerationPlugin.kt) and JvmElementActionsFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/lang/jvm/actions/JvmElementActionsFactory.kt) classes, but they are currently not recommended for external usage.
The base element of UAST is UElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt). All common base sub-interfaces are located in the declarations (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/declarations) and expressions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/expressions) directories of the uast module.
All these sub-interfaces provide methods to get the information about common syntax elements: UClass (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/declarations/UClass.kt) about class declarations, UIfExpression (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt) about conditional expressions, and so on.
To obtain UAST for given PsiElement of one of supported languages, use UastFacade (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/UastContext.kt) class or UastContextKt.toUElement() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/UastContext.kt):
UastContextKt.toUElement(element);element.toUElement()To convert PsiElement to the specific UElement, use one of the following approaches:
for simple conversion:
UastContextKt.toUElement(element, UCallExpression.class);element.toUElement(UCallExpression::class.java)for conversion to one of different given options:
UastFacade.INSTANCE.convertElementWithParent(element,
new Class[]{UInjectionHost.class, UReferenceExpression.class});UastFacade.convertElementWithParent(element,
UInjectionHost::class.java, UReferenceExpression::class.java)in some cases, PsiElement could represent several UElements. For instance, the parameter of a primary constructor in Kotlin is UField and UParameter at the same time. When needing all options, use:
UastFacade.INSTANCE.convertToAlternatives(element,
new Class[]{UField.class, UParameter.class});UastFacade.convertToAlternatives(element,
UField::class.java, UParameter::class.java)It is always better to convert to the specific type of UElement, rather than to convert without type and then cast to the specific type:
Because of performance: toUElement() with type is fail-fast
Because of possibly getting different results in some cases: conversion with type is more predictable
Sometimes it's required to get from the UElement back to sources of the underlying language. For that purpose, UElement#sourcePsi property returns the corresponding PsiElement of the original language.
The sourcePsi is a "physical" PsiElement, and it is mostly used for getting text ranges in the original file (e.g., for highlighting). Avoid casting the sourcePsi to specific classes because it means falling back from the UAST abstraction to the language-specific PSI. Some UElement are "virtual" and thus do not have sourcePsi. For some UElement, the sourcePsi could be different from the element from which the UElement was obtained.
Also, there is a UElement#javaPsi property that returns a "Java-like" PsiElement. It is a "fake" PsiElement to make different JVM languages emulate Java language to keep compatibility with Java-API. For instance, when calling MethodReferencesSearch.search(PsiMethod), only Java natively provides PsiMethod; other JVM languages thus provide a "fake" PsiMethod via UMethod#javaPsi.
Note that UElement#javaPsi is physical for Java only. Thus UElement#sourcePsi should be used to obtain text-range or an anchor element for inspection warnings/gutter marker placement.
In short:
sourcePsi:
is physical: represents a real existing PsiElement in the sources of the original language
can be used for highlighting, PSI modifications, creating smart-pointers, etc.
should not be cast unless absolutely required (for instance, handling a language-specific case)
javaPsi:
should be used only as a representation of JVM-visible declarations: PsiClass, PsiMethod, PsiField for getting their names, types, parameters, etc., or to pass them to methods that accept Java-PSI declarations
not guaranteed to be physical: could not exist in sources
is not modifiable: calling modification methods could throw exceptions for non-Java languages
Note: both sourcePsi and javaPsi can be converted back to the UElement.
In UAST there is no unified way to get children of the UElement (though it is possible to get its parent via UElement#uastParent). Thus, the only way to walk the UAST as a tree is passing the UastVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/visitor/UastVisitor.kt) to UElement.accept() method.
Note: there is a convention in UAST-visitors that a visitor will not be passed to children if visit*() returns true. Otherwise, UastVisitor will continue the walk into depth.
UastVisitor can be converted to PsiElementVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElementVisitor.java) using UastVisitorAdapter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/uast/UastVisitorAdapter.java) or UastHintedVisitorAdapter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/uast/UastHintedVisitorAdapter.kt). The latter is preferable as it offers better performance and more predictable results.
As a general rule, it's recommended to abstain from using UastVisitor: if you don't need to process many UElements of different types and if the structure of elements is not very important, then it is better to walk the PSI-tree using PsiElementVisitor and convert each PsiElement to its corresponding UAST explicitly via UastContext.toUElement().
UAST is not a zero-cost abstraction: some methods (https://youtrack.jetbrains.com/issue/KT-29856) could be unexpectedly expensive for some languages, so be careful with optimizations because it could yield the opposite effect.
Converting to UElement also could require resolve for some languages in some cases, again, possibly unexpectedly expensive. Converting to UAST should be performed only when necessary. For instance, converting the whole PsiFile to UFile and then walk it solely to collect UMethod declarations is inefficient. Instead, walk the PsiFile and convert each encountered matching element to UMethod explicitly.
UAST is lazy when you pass visitors to UElement.accept() or getting UElement#uastParent.
For really hard performance optimisation consider using UastLanguagePlugin.getPossiblePsiSourceTypes() to pre-filter PsiElements before converting them to UAST.
ULiteralExpression (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt) represents literal values like numbers, booleans, and string. Although string values are also literals, ULiteralExpression is not very handy to work with them. For instance, it doesn't handle Kotlin's string interpolations. To process string literals when evaluating their value or to perform language injection, use UInjectionHost (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/uast/uast-common/src/org/jetbrains/uast/expressions/UInjectionHost.kt) instead.
For historical reasons, the relations between UElement and PsiElement are complicated. Some UElements implement PsiElement; for instance, UMethod implements PsiMethod. It is strongly discouraged to use UElement as PsiElement, and Plugin DevKit provides a corresponding inspection (Plugin DevKit | Code | UElement as PsiElement usage). This "implements" is considered deprecated and might be removed in the future.
Also, there is UElement#psi property; it returns the same element as javaPsi or the sourcePsi. As it is hard to guess what will be returned, it is also deprecated.
Thus sourcePsi and javaPsi should be the only ways to obtain PsiElement from UElement. See the corresponding section.
UAST provides a unified way to represent JVM compatible declarations via UMethod, UField, UClass, and so on. But at the same time, all JVM language plugins implement PsiMethod, PsiClass, and so on to be compatible with Java. These implementations could be obtained via UElement#javaPsi property.
So the question is: "What should I use to represent the Java-declaration in my code?". The answer is: We encourage using PsiMethod, PsiClass as common interfaces for Java-declarations regardless of the JVM language and discourage exposing the UAST interfaces in the API.
Note: for method bodies, there are no such alternatives, so exposing, for instance, the UExpression is not discouraged. Still, consider exposing the raw PsiElement instead.
UAST is an abstraction level on top of PSI of different languages and tries to build a unified tree (see Inspecting UAST Tree). It leads to the fact that the tree structure could seriously diverge between UAST and original language, so no ancestor-descendant relation preserving is guaranteed.
For instance, the results of:
generateSequence(uElement, UElement::uastParent).mapNotNull { it.sourcePsi }
generateSequence(uElement.sourcePsi) { it.parent }could be different, not only in the number of elements, but also in their order.
To use UAST in your plugin, add a dependency (Plugin Dependencies) on bundled Java plugin (com.intellij.java).
To register extensions (Extensions) applicable to UAST, specify language="UAST" in their registration in plugin.xml (Plugin Configuration File).
To inspect UAST Tree, invoke internal action (Enabling Internal Mode) Tools | Internal Actions | UAST | Dump UAST Tree (By Each PsiElement).
Use AbstractBaseUastLocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseUastLocalInspectionTool.java) as base class and specify language="UAST" in registration. If inspection targets only a subset of default types (UFile, UClass, UField, and UMethod), specify UElements as hints in overloaded constructor to improve performance.
Use ProblemsHolder.registerUProblem() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/codeInspection/problemHolderUtil.kt) extension functions for registering problems (2023.2).
Use UastUtils.getUParentForIdentifier() or UAnnotationUtils.getIdentifierAnnotationOwner() for annotations to obtain suitable "identifier" element (see Line Marker Provider (8. Line Marker Provider) for details).
This article is intended for plugin writers who create custom web server integrations, or some UI for easy XML editing. It describes the Document Object Model (DOM) in IntelliJ Platform - an easy way to work with DTD or Schema-based XML models. The following topics will be covered: working with DOM itself (reading/writing tags content, attributes, and subtags) and easy XML editing in the UI by connecting UI to DOM.
It's assumed that the reader is familiar with Java, Swing, IntelliJ Platform XML PSI (classes XmlTag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlTag.java), XmlFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlFile.java), XmlTagValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlTagValue.java), etc.), IntelliJ Platform plugin development basics (application and project components, file editors (Editors)).
So, how to operate with XML from an IntelliJ Platform plugin? Usually, one has to take XmlFile, get its root tag, and then find a required sub-tag by path. The path consists of tag names, each of them a string. Typing these everywhere is tedious and error-prone. Let's assume you have the following XML:
<root>
<foo>
<bar>42</bar>
<bar>239</bar>
</foo>
</root>Let's say you want to read the contents of the second bar element, namely, "239".
It's not correct to create chained calls like
file.getDocument()
.getRootTag()
.findFirstSubTag("foo")
.findSubTags("bar")[1]
.getValue()
.getTrimmedText();because each call here may return null.
So the code would probably look like this:
XmlFile file = ...;
XmlDocument document = file.getDocument();
if (document != null) {
XmlTag rootTag = document.getRootTag();
if (rootTag != null) {
XmlTag foo = rootTag.findFirstSubTag("foo");
if (foo != null) {
XmlTag[] bars = foo.findSubTags("bar");
if (bars.length > 1) {
String s = bars[1].getValue().getTrimmedText();
// do something
}
}
}
}Looks awful, doesn't it? But there's a better way to do the same thing. You just need to extend a special interface - DomElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomElement.java).
For example, let's create several interfaces:
interface Root extends com.intellij.util.xml.DomElement {
Foo getFoo();
}
interface Foo extends com.intellij.util.xml.DomElement {
List<Bar> getBars();
}
interface Bar extends com.intellij.util.xml.DomElement {
String getValue();
}Next, you should create a DomFileDescription (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomFileDescription.java) class, pass to its constructor the root tag name and root element interface. Register it in plugin.xml (Plugin Configuration File) using com.intellij.dom.fileMetaData extension point and specify rootTagName and domVersion/stubVersion attributes.
When targeting 2019.1 or earlier, use com.intellij.dom.fileDescription extension point instead.
You can now get the file element from DomManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomManager.java). To get the "239" value, you only have to write the following code:
DomManager manager = DomManager.getDomManager(project);
Root root = manager.getFileElement(file).getRootElement();
List<Bar> bars = root.getFoo().getBars();
if (bars.size() > 1) {
String s = bars.get(1).getValue();
// do something
}I suppose this looks a little nicer. You often work with your model in more than one place. Re-creating the model is too inefficient, so we cache it for you, and any subsequent calls to DomManager.getFileElement() will return the same instance. So, it is useful to invoke this method just once, and then keep everywhere only the "root" object you've obtained. In this case, you won't need to repeat that scary first line, and the code will look even nicer.
It is also important to note that with this scenario we avoid potential NullPointerException: our DOM guarantees that every method accessing a tag child will return a not-null element, even if the correspondingly-named sub-tag doesn't exist. That may seem strange at first glance, but it appears to be rather convenient. How does it work? Simple. Given those interfaces, DOM generates all the code for accessing correct subtags and creating model elements at runtime. The sub-tag names and element types are taken from method names, return types and method annotations, if any. In most cases annotations can be omitted, as in our example, but this is discussed further in this article anyway.
Now let us explore more thoroughly what the DOM can do, and look at possible ways of representing various XML concepts such as tag content, attributes or sub-tags. Later, we will discuss basic methods for working with the model, as well as cover more advanced functionality. Finally, we'll see how to easily create a UI editor for DOM model elements.
In XML PSI, tag content is referred to as tag value, so well do the same for consistency. To read and change a tag value, you have to add two methods (getter and setter) to your interface, like this:
String getValue();
void setValue(String s);These method names (getValue and setValue) are standard, and they are used for accessing tag values by default. If you want to use custom method names for the same goal, you should annotate these methods with @TagValue, for example:
@TagValue
String getTagValue();
@TagValue
void setTagValue(String s);As you can see, our accessors work with String values. This is natural, since XML represents a text format, and tag content is always text. But sometimes you may want to operate with integers, booleans, enums, or even class names (they, of course, will be represented as PsiClass), and more generic Java types (PsiType). In such cases, you just need to change the type in methods to the one you need, and everything will keep working correctly.
If you operate with even more exotic types, you should tell DOM how to deal with them. First, annotate your accessor methods with the @Convert annotation, and specify your own class that should extend the Converter<T> class in the annotation. Here T is your exotic type, while Converter<T> is a thing that knows how to convert values between String and T. If the value cannot be converted (for example, "foo" is not convertible into Integer), the converter may return null. Please also note that your implementation should have a no-argument constructor.
Let us consider an interesting case when T represents an enum value. Usually, the converter just searches for enum elements with the names specified in XML. But sometimes, for their names, you may need or want to use values that are not valid Java identifiers. For example, the CMP version in EJB may be "1.x" or "2.x", but you can't create Java enums with such names. For such cases, let your enum implement NamedEnum interface, and then name your enum elements as you wish. Now, just provide the getValue() implementation that will return the right value to match with XML contents, and voilà! In our example, the code will look as follows:
enum CmpVersion implements NamedEnum {
CmpVersion_1_X ("1.x"),
CmpVersion_2_X ("2.x");
private final String value;
CmpVersion(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}As we have already mentioned, an XML tag may have lots of artifacts besides its value: there can be attributes, children, but rather often (e.g., according to DTD or Schema) it should have only the value. Of course, such tags also need a DOM element to associate with. And we provide such an element:
interface GenericDomValue<T> {
T getValue();
void setValue(T t);
@TagValue
String getStringValue();
@TagValue
void setStringValue(String s);
}So, you can just specify a particular T when using this interface - and everything will work. Methods that work with String are provided for many reasons. For example, your T is PsiClass (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiClass.java). It would be useful to highlight invalid values in the UI. To get the value to highlight (the string from the XML file), we have the getStringValue() method. The error message will be taken from the converter via getErrorMessage().
Attributes are also rather simple to deal with. You can read their values, set them, and operate with different types. So it's natural to create something like GenericDomValue<T> and then work as usual. "Something like" will be an inheritor, as shown below:
interface GenericAttributeValue<T> extends GenericDomValue<T> {
XmlAttribute getXmlAttribute();
}Consider that you want to work with an attribute named some-class having a value of type PsiClass:
@Attribute("some-class")
GenericAttributeValue<PsiClass> getMyAttributeValue();That's all! Now you can get/set values, resolve this PsiClass, get its String representation, etc. The name of the attribute will be taken from the method name (see next paragraph). If you name your method in a special way, you can even omit the annotation. For example:
GenericAttributeValue<PsiClass> getSomeClass();The DomNameStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomNameStrategy.java) interface specifies how to convert accessor names to XML element names. Or more precisely, not the full accessor names, but rather the names minus any "get", "set" or "is" prefixes. The strategy class is specified in the @NameStrategy annotation in any DOM element interface. Then any descendants and children of this interface will use this strategy. The default strategy is HyphenNameStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/HyphenNameStrategy.java), where words are delimited by hyphens (see sample above). Another common variant is JavaNameStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/JavaNameStrategy.java) that capitalizes the first letter of each word, as in Java's naming convention. In our example, the attribute name would be "someClass".
If attribute doesn't define a PsiClass, but some other custom T that needs a converter, you just need to specify the @Convert annotation to the getter.
Please note that the attributes' getter method will never return null, even if the attribute isn't specified in XML. Its getValue(), getStringValue() and getXmlAttribute() methods will return null, but the DOM interface instance will exist and be valid. If the element has an underlying attribute, this can be easily fixed (surely, only if you need that): just call the undefine() method (defined in DomElement), and the XML attribute disappears, while GenericAttributeValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/GenericAttributeValue.java) remains valid.
You may often deal with tags that have at most one sub-tag with the given name (e.g. <ejb-name>, <ejb-class> or <cmp-field>) in tags defining entity EJBs. To work with such children, provide getters for them. These getters should have a return type that extends DomElement:
GenericDomValue<String> getEjbName();
GenericDomValue<String> getEjbClass();
CmpField getCmpField();There's also an annotation to designate such children explicitly: @SubTag. Its "value" attribute contains a tag name. If it is not specified, the name is implied from the method name using the current name strategy.
Sometimes it is the sub-tag's presence that means something, rather than its content - <unchecked> in EJB method permissions, for example. If it exists, then permissions are unchecked, otherwise checked. For such things one should create a special GenericDomValue<Boolean> child. Usually its getValue() returns true if there's "true" in a tag value, false if there's "false" in a tag value, and null otherwise. In the @SubTag annotation, you can specify the attribute like indicator=true. In this case, getValue() will return true if the tag exists and false otherwise.
Let's consider another interesting example inspired by EJB, where there is a relation that has two roles, each designating one relation end: first role and second role. Both are represented by tags with the same values. So, we could create a collection of role elements, and every time we access some role we would check if this collection has a sufficient number of elements. But one of the main purposes of the DOM is to eliminate unnecessary checks. So why can't we have a fixed (more than one) number of children with the same tag name? Let's have them!
@SubTag(value = "ejb-relationship-role", index = 0)
EjbRelationshipRole getEjbRelationshipRole1();
@SubTag(value = "ejb-relationship-role", index = 1)
EjbRelationshipRole getEjbRelationshipRole2();The first method will return the DOM element for the first subtag named <ejb-relationship-role>, and the second - for the second one. Hence, the term "fixed number" for such children. According to DTD or Schema, there should be a fixed number of subtags with the given name. Most often this fixed number is 1; in our case with the relations it is 2. Just like attributes, fixed-number children exist regardless of underlying tag existence. If you need to delete tags, it can be done with the help of the same undefine() method.
For children of GenericDomValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/GenericDomValue.java) type, you can also specify a converter, just as you can for attributes.
One more common case in DTD and Schemas is when children have the same tag name and a non-fixed upper limit in count. Their accessors differ from those of the fixed-number children in the following: the return result is Collection or List of a special type that extends DomElement, and if you want to use name strategies, the method name must be in pluralized form. For example, in the EJB we would have the following method:
List<Entity> getEntities();There's also an annotation @SubTagList where you can explicitly specify the tag name.
Returned collections cannot be modified directly. To delete an element from a collection, just call undefine() on this element. The tag will then be removed, and an element will become invalid (DomElement.isValid() == false). Note that this behavior differs from that of fixed-number children and attributes: they are always valid, even after undefine(). Again, unlike those children types, collection children always have valid underlying XML tags.
Adding elements is a bit harder. Since all DOM elements are created internally, you can't just pass some of your DOM elements to some method to add the element to the collection. In fact, you have to ask a parent element to add a child to the collection. In our example, it's done in the following way:
Entity addEntity(int index);which adds an element to wherever you want, or
Entity addEntity();which adds a new DOM element to the end of the collection. Please note the singular tense of the word "Entity". That's because here we deal with one Entity object, while in the collection getter we dealt with potentially many entities.
Now, you can do anything you want with the returned value: modify, define the tag's value, children, etc.
The last common case is also a collection, but one consisting of tags with different names that are arbitrarily mixed. To work with it, you should define collection getters for all tag names within the mixed collection, and then define an additional specially annotated getter:
// <foo> elements
List<Foo> getFoos();
// <bar> elements
List<Bar> getBars();
// all <foo> and <bar> elements
@SubTagsList({ "foo", "bar" })
List<FooBar> getMergedListOfFoosAndBars();The annotation here is mandatory - we cannot guess several tag names from one method name.
To add elements to such mixed collections, you should create "add" methods for each possible tag name:
@SubTagsList(value = { "foo", "bar" }, tagName = "foo")
FooBar addFoo();
@SubTagsList(value = { "foo", "bar" }, tagName = "bar")
FooBar addBar(int index);The index parameter in the last example means the index in the merged collection, not in the collection of tags named "bar".
You can extend existing DOM model at runtime by implementing com.intellij.util.xml.reflect.DomExtender<T>. Register it in "extenderClass" attribute of com.intellij.dom.extender extension point, where "domClass" specifies DOM class <T> to be extended. DomExtensionsRegistrar (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomExtensionsRegistrar.java) provides various methods to register dynamic attributes and children.
If the contributed elements depend on anything other than plain XML file content (used framework version, libraries in classpath, ...), make sure to return false from DomExtender.supportsStubs().
Annotate DOM model with Namespace (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/Namespace.java) and register namespace key mapping via DomFileDescription.registerNamespacePolicy() from DomFileDescription.initializeFileDescription().
Plugin DevKit supports the following features for working with DOM related code:
DomElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomElement.java) - provide implicit usages for all DOM-related methods defined in inheriting classes (to suppress "unused method" warning)
DomElementVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomElementVisitor.java) - provide implicit usages for all DOM-related visitor methods defined in inheriting classes (to suppress "unused method" warning)
It often happens that a collection contains same-named tags that may have different structure or even be represented by different types in the DTD or Schema. As an example, JSF Managed Beans may be of three types. If a <managed-bean> tag contains a <map-entries> sub-tag, then the Managed Bean type is MapEntriesBean. If it contains a <list-entries> sub-tag - can you guess? Right - ListEntriesBean! Otherwise, it's a PropertyBean (all three interfaces extend ManagedBean). And when we write List<ManagedBean> getManagedBeans(), we expect to get not only a list where all elements are instances of the ManagedBean interface, but a list where each element is of a certain type, i.e. MapEntriesBean, ListEntriesBean, or PropertyBean.
In such cases, one should decide which interface the DOM element should actually implement (according to the given tag). This is achieved by extending the TypeChooser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/TypeChooser.java) abstract class:
public abstract class TypeChooser {
public abstract Type chooseType(XmlTag tag);
public abstract void distinguishTag(XmlTag tag, Type aClass)
throws IncorrectOperationException;
public abstract Type[] getChooserTypes();
}Here, the first method (chooseType()) does exactly what it is named after (chooses the particular type, most often it's a class). The second one (distinguishTag()) acts in reverse: it modifies a tag so that when the element is read from an XML file next time (for example, after the user has closed and opened the project again), the newly created DOM element will implement the same interface and no model data will be lost. Finally, getChooserTypes() just returns all the types that could be returned by chooseType().
To make your TypeChooser work, register it in your overridden DomFileDescription.initializeFileDescription() method by calling registerTypeChooser().
Of course, DOM is tightly connected to XML PSI, so there's always a way of getting the XmlTag instance (which can be null for fixed-number children and attributes) using the getXmlTag() method. We remember that in GenericAttributeValue there's also the getXmlAttribute() method. In general case there is getXmlElement() method. You can also get a DOM element by its underlying XML PSI element using the DomManager.getDomElement() method.
If a DOM element has no underlying XML element, it can be created by calling ensureTagExists(). To delete a tag, use the already known undefine() method. This method will always delete the underlying XML element (tag or attribute). If the element was a collection's child, then neither it nor its entire subtree will be valid anymore.
In every normal tree, there's always a possibility to walk up. DomElement is no exception. Method getParent() just returns element's parent in tree.
The method <T extends DomElement> T getParentOfType(Class<T> requiredClass, boolean strict) returns the tree ancestor of the given class. You can see the standard strict parameter, that can return the DOM element itself, if it's false and your current DOM element is an instance of requiredClass.
Finally, getRoot() will return the DomFileElement, which is the root of every DOM tree.
An element becomes invalid if it has been deleted explicitly or due to external PSI changes. Fixed-number children and attributes are meant to stay valid as long as possible, no matter what happens with XML. They can become invalid only if they have a collection tree ancestor that has been deleted.
Newly created DOM elements are always correct and valid, so their isValid() methods will return true.
Element validity is very important, since you cannot invoke any methods on invalid elements (except, of course, isValid() itself).
DOM also has a kind of reflection, called "Generic Info". One would use it to be able to access children by tag names directly, instead of calling getter methods. See DomGenericInfo (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomGenericInfo.java) interface and getGenericInfo() methods in DomElement and DomManager for more information. There's also DomElement.getXmlElementName() method that returns the name of a corresponding tag or attribute.
DomElement.getPresentation() returns an instance of ElementPresentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ElementPresentation.java), an interface that knows presentable element type, name, and sometimes even its icon. Presentations are actually obtained from presentation factory objects that, like ClassChoosers, should be registered in ElementPresentationManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ElementPresentationManager.java) as early as possible. You can specify type name and icon for all elements of some class, ways of getting type name, icon and presentable name for particular objects. When not specified, presentable name is taken from the object itself, if it contains a method annotated with @NameValue annotation, that returns String or GenericValue. If there's no such method, it will return null. For DomElement, there's another way to get this presentable name: DomElement.getGenericInfo().getElementName().
If you want to be notified on every change in the DOM model, add DomEventListener to DomManager. DOM supports the following events: tag value changed, element defined/undefined/changed, and collection child added/removed.
The DOM supports error checking and highlighting. It's based on annotations which you add to the DOM element in a special place (don't confuse these annotations with the ones of Java 5 - they are very different). You need to implement the DomElementAnnotator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/highlighting/DomElementsAnnotator.java) interface, and override DomFileDescription.createAnnotator() method, and create this annotator there. In DomElementsAnnotator.annotate(DomElement element, DomElementsProblemsHolder annotator) you should report about all errors and warnings in the element's subtree to the annotator (DomElementsProblemsHolder.createProblem()). You should return this annotator in the corresponding virtual method of the DomFileDescription.
The following errors can be highlighted automatically by providing an instance of BasicDomElementsInspection:
@Required element missing or having empty text
XML value cannot be converted by some Converter
name is not unique while it should be
The latter case requires you to specify the name getter with @NameValue annotation. The checking uses the DomFileDescription.getIdentityScope() method to get the element defining the root scope in which the name should be unique.
To suppress spellchecking annotate your DomElement with @com.intellij.spellchecker.xml.NoSpellchecking.
There is a common case in error highlighting, when one needs to say, that some required sub-tag or attribute is missing. DOM will do this for you automatically, if you annotate the getter for that child with the @Required annotation. For collection children getters, this annotation will mean that the collection should be not empty (corresponding to '+' sign in DTD). Also, when you create a new element that has required fixed-number or attribute children, their tags or attributes will also be created in XML.
Remember the interface GenericDomValue<T> and its sub-interface GenericAttributeValue<T>? Remember, that ANY class may be passed as T - for example, let's interpret GenericDomValue<PsiClass> as a reference to a class. Then we can always consider it as a reference to an object of class T! With strings or enums, it is not a very useful idea, but we'll use it in another way. Very often XML has such a structure that an object is declared at some place, and is referenced at some other place (more precisely, in a tag or attribute value). So, if you want to create a method like GenericValue<MyDomElement> getMyDomElementReference(), then you just have to specify a proper converter that will find an instance in your model of MyDomElement with the name specified in the GenericDomValue.
That's the core idea. Since creating such converters is quite boring, we've done it for you. You don't have to annotate reference getters at all, as the name resolution will be made automatically. Elements will be searched by name, and the name will be taken from the method annotated with @NameValue. The converter used is DomResolveConverter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/DomResolveConverter.java). Its constructor takes a parameter, so it can't be referenced in @Convert annotation, but its subclasses (if you create them) - can. If you still want to specify explicitly that your reference to DomElement should be resolved "model-wide", use the @Resolve annotation parameterized with the desired class. The resolution scope will be taken from the DomFileDescription.getResolveScope().
In addition to the above, auto-resolving in DOM also provides some features in your XML text editor: error highlighting, completion, Find Usages, Rename Refactoring... Unresolved references will be highlighted, and even completed. If you want to create a custom converter and want to have this code insight with it, you should extend not only the Converter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/Converter.java) but ResolvingConverter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ResolvingConverter.java). It has one more method getVariants(), where you'll have to provide the collection consisting of all targets your reference may resolve to. Those familiar with PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) will recognize the similarities here.
If you need to choose a Converter depending on other values (e.g. in a sibling/parent element) or any runtime condition (e.g. presence or version of a library), you can use WrappingConverter. See also GenericDomValueConvertersRegistry for managing an extensible registry of available Converters to choose from.
Your DOM elements do not have to be tied to a physical file. DomManager.createMockElement() will help you to create a virtual element of a given class with the given module. An element may be physical or not. 'Physical' here means that DOM will create a mock document for it, so you can enjoy Undo functionality if you pass this document to the right place in the file editor.
DomElement.copyFrom() allows you to copy information from one DomElement to another. In fact, it just replaces XML tags, and all the old data is lost. Nevertheless, the element's fixed-number children don't become invalid. They only contain new tag values, attribute values, etc. The tree is actually rather conservative.
The combination of createMockElement() and copyFrom() is useful for editing element contents in dialogs. You create a mock copy of an element, work with it in the dialog and then, if the user doesn't cancel, copy the element back to the main model. Since it's a common case, a special shortcut method has been created in DomElement, called createMockCopy().
IntelliJ Platform's XML parser is incremental: changes in a text do not cause the whole file to be reparsed. But you should keep in mind that this rule may sometimes not work correctly. For example, your DOM elements can unexpectedly become broken as a result of manual editing of the XML file (even if it didn't happen inside those elements). If a file editor depends on such a broken element, this can lead to closing the tab, which isn't very nice from the user's point of view. For example, suppose you have an entity bean named "SomeEntity". You open an editor for it, then you go into the XML, change the tag name from entity to session, and then back to entity. Of course, no DOM element can survive after such blasphemy. But notwithstanding, you still want your editor to stay open! Well, there is a solution, and it's called DomManager.createStableValue(Factory factory). This method creates a DOM element that delegates all its functionality to some real element (returned from the factory parameter). As soon as that real element becomes invalid, the factory is called once more, and if it returns something valid, it becomes the new delegate. And so on... In the example with EJB, the factory would once again look for an Entity Bean named "SomeEntity".
Stable DOM elements also implement the StableElement interface, which has the following methods:
DomElement getWrappedElement() - just returns the current element to which all method calls are delegated;
void invalidate() - makes the wrapped element invalid. Any following method call will cause the factory to create a new delegate;
void revalidate() - calls the factory, and if it returns something new (i.e. not the same as the current wrapped element) invalidates the old value and adopts the new one.
Visitor is a very common design pattern. DOM model also has a visitor, and it's called DomElementVisitor. The DomElement interface has methods accept() and acceptChildren() that take this visitor as a parameter. If you look at the interface DomElementVisitor itself, you may be surprised, since it has only one method: visitDomElement(DomElement). Where is the visitor pattern? Where are all those methods with names like visitT(T) that are usually found in it? There are no such methods, because the actual interfaces (T's) aren't known to anyone except you. But when you instantiate the DomElementVisitor interface, you may add there these visitT() methods, and they will be called! You may even name them just visit(), specify the type of the parameter, and everything will be fine. For example, if you have two DOM element classes - Foo and Bar - your visitor may look like this:
class MyVisitor implements DomElementVisitor {
void visitDomElement(DomElement element) {}
void visitFoo(Foo foo) {}
void visitBar(Bar bar) {}
}Sometimes you may want to extend your model with some functionality that isn't directly connected with XML, but relates to your program logic. And the most appropriate place for this functionality is the DOM element interface. What to do then?
The simplest case is when you want to add to your interface a method that returns exactly what some other getter in this element (or in one of its children) returns. You can easily write this helper method and annotate it with the @PropertyAccessor annotation, in which you should specify the path consisting of property names (getter names without the "get" or "is" prefixes). For example, you can write:
GenericDomValue<String> getVeryLongName();
@PropertyAccessor("very-long-name")
GenericDomValue<String> getName();In this case, the second method will return just the same as the first one. If there were "foo.bar.name" instead of "very-long-name" in the annotation, the system would actually call getFoo().getBar().getName() and return the result to you. Such annotations are useful when you're extending some interface that is inconsistent with your model, or you try to extract a common super-interface from two model interfaces with differently named children that have the same sense (see <ejb-ref> and <ejb-local-ref>).
The case just described is simple, but rare. More often, you really have to incorporate some logic into your model. Then nothing except Java code helps you. And it will. Add the desired methods to your interface, then create an abstract class implementing the interface, and implement there only methods that you added manually and that are not directly connected to your XML model. Note that the class should have a constructor with no arguments.
Now you only have to let DOM know that you wish to use this implementation every time you're creating a model element that should implement the necessary interface. Simply register it using com.intellij.dom.implementation extension point and DOM will generate at run-time the class that not only implements the needed interface, but also extends your abstract class.
Many frameworks require a set of XML configuration files ("fileset") to work as one model, so resolving/navigation works across all related DOM files. Depending on implementation/plugin, providing filesets implicitly (using existing framework's setup in a project) or via user configuration (usually via dedicated Facet) can be achieved.
Extend DomModelFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/model/impl/DomModelFactory.java) (or BaseDomModelFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/model/impl/BaseDomModelFactory.java) for non-Module scope) and provide implementation of your DomModel. Usually you will want to add searcher/utility methods to work with your DomModel implementation.
Please use it sparingly and only for heavily accessed parts in your DOM model, as it increases disk space usage/indexing run time.
DOM elements can be stubbed, so (costly) access to XML/PSI is not necessary (see Indexing and PSI Stubs (Indexing and PSI Stubs) for similar feature for custom languages). Performance relevant elements, tag or attribute getters can simply be annotated with @com.intellij.util.xml.Stubbed. Set and increase stubVersion of com.intellij.dom.fileMetaData extension whenever you change @Stubbed annotations usage in your DOM hierarchy to trigger proper rebuilding of Stubs during indexing.
This API is unmaintained and will likely be removed in future versions.
All forms that deal with DOM are organized in a special way. They support two main things: getting data from XML into the UI, and saving UI data to XML. The former is called resetting, the latter - committing. There's Committable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/Committable.java) interface that has corresponding methods: commit() and reset(). There's also a way of structuring your forms into smaller parts, namely the Composite pattern: CompositeCommittable (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/CompositeCommittable.java). Methods commit() and reset() are invoked automatically on editor tab switch or undo. So you only need to ensure that all your Swing structure is organized in a tree of CompositeCommittable, and all the hard work will be done by the IDE.
DOM controls are special descendants of Committable. All of them implement DomUIControl. Note that they are not Swing components - they are only a way of connecting DOM model and Swing components. One end of the connection - the DOM element - is usually specified in the control's constructor. The other end - Swing component - can be obtained in 2 ways. The first is to ask DOM control to create it. But that is rather inconvenient if you want to create the forms in, say, IntelliJ IDEA's GUI Designer. In that case, you'll need the second way: ask the control to bind() to an existing Swing component of a correct type (that depends on the type of value that you're editing). After that, your Swing components will be synchronized with DOM. They'll even highlight errors reported by DomElementsAnnotator.
Sometimes you may need to do some work (enable or disable some components, change their values) after a particular DOM control is committed. Then you should define the addCommitListener() method of that DOM control and override the CommitListener.afterCommit() method. This method will be invoked inside the same write action as the main commit(), so any changes you do in this method to the XML will be merged with the commit() in the Undo queue.
With simple controls, you can edit GenericDomValue: simple text, class names, enums and boolean values. These controls take a special object as a constructor parameter. This object should implement the DomWrapper interface that knows how to set/get values to/from a DOM model.
We have three major DomWrapper's: DomFixedWrapper<T> (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/DomFixedWrapper.java) redirecting calls to GenericDomValue<T> (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/GenericDomValue.java), DomStringWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/DomStringWrapper.java) redirecting calls to string accessors of GenericDomValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/GenericDomValue.java), and DomCollectionWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/DomCollectionWrapper.java) that gets/sets values of the first element of the given GenericDomValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/GenericDomValue.java) collection. Some controls (those having a text field as part of itself) take an additional boolean constructor parameter - commitOnEveryChange, whose meaning is evident from the name. We don't recommend using it anywhere except small dialogs, because committing on every change slows down the system significantly.
Most often these controls are created by DomUIFactory.createControl(GenericDomValue). This method understands which control to create by using DOM reflection (DomGenericInfo, as you probably remember). But sometimes you may want to create the controls directly. So let's look at the simple controls more closely.
It allows you to edit boolean values. The control is bound to JCheckBox.

The control is bound to a non-editable JComboBox, so it can be used to choose something from a limited set. One case of such a limited set is enum. Or it can be a constructor where you can provide a Factory<List<String>>, and return from this factory anything you want (for example, a list of database names to choose from). By default, the wrong values (written in XML, but not present in the list you've given to the control) are displayed in red. Since it's common practice to specify custom CellRenderer for combo boxes, the control has the isValidValue(String) method. If it returns false on the value you're rendering, you can highlight it in some way, to achieve the same result as the default renderer. Or you can just delegate to that renderer in your own way.

Sometimes, when there are only 2 alternatives, it's convenient to use a checkbox instead of a combobox. This control is designed specially for such cases. While being (and being bound to) a checkbox, the control edits not just "true" or "false", but any two String values, or two enum elements. In the last case, it has a boolean invertedOrder parameter, to specify which element corresponds to the checked state. By default, invertedOrder is set to false, so the first element corresponds to the unchecked state, and the second - to the checked one. If you set the parameter to true, the states will swap.
Please note that editor-based controls are built on IntelliJ Platform's Editor instead of standard JTextField. Since there's currently no way to instantiate Editor directly through the Open API, controls are bound to special JPanel inheritors, and their bind() method adds the necessary content to those panels.
This control allows you to edit simple string values. The control is bound to a TextPanel component. There's also an inheritor of that panel - MultiLineTextPanel. If you bind a StringControl to it, a big editor will appear on the screen. In case you don't have space for a big editor, bind it to a BigTextPanel. Then it will be filled with a text editor, and the browse button will be added to open a dialog with the big editor where you can type a longer string.
This is a one-line editor with a browse button that opens the standard class selection dialog. The control accepts class names only. It is bound to PsiClassPanel.

This is almost the same as PsiClassControl, but allows entering not only class names, but also Java primitive types and even arrays. It is bound to PsiTypePanel.
There is a special table component where each row represents one collection child. It's called DomCollectionControl<T>, where T is your collection element type. To function properly, it needs DomElement (parent of the collection), some description of the collection (sub-tag name or a DomCollectionChildDescription from DOM reflection), and a ColumnInfo array. This can be passed to the constructor, or can be created in a DomCollectionControl inheritor, in an overridden method createColumnInfos().
What is a column info? It's just a somewhat more comfortable way to work with the table model. It uses Java 5 generics and is more object-oriented. So, it's named ColumnInfo<Item,Aspect>, where Item is a type variable corresponding to the type of elements in the collection, and Aspect is a type variable corresponding to this particular column information type: String, PsiClass, Boolean, etc. The basic things that a column knows are: column name, column class, reading value (Aspect valueOf(Item)), writing value (setValue(Item item, Aspect aspect)), cell renderer (getRenderer(Item)), cell "editability" (isCellEditable(Item)), cell editor (getEditor(Item)), etc.
There are a lot of predefined column infos, so you'll probably never create a new one.
First, if a collection child is a GenericDomValue, it's usually convenient to edit it directly in the table. For this, you may need one of the following classes: StringColumnInfo, BooleanColumnInfo, or more generic GenericValueColumnInfo. But such collections are encountered very rarely.
A more common case is when a collection element is more complex and has several GenericDomValue children. Then one may create a column for each of those children. The appropriate column info is ChildGenericValueColumnInfo<T>. It will ask you for a DomFixedChildDescription (one more thing from DOM reflection), a renderer and an editor - nothing else. So, the main things left to customize are the renderer and the editor.
As for the renderer, there are two main choices: DefaultTableCellRenderer, and IntelliJ Platform's BooleanTableCellRenderer. Editors are more complicated, but they closely resemble simple DOM controls.
BooleanTableCellEditor, DefaultCellEditor(JTextField), ComboTableCellEditor, etc. DomUIFactory.createCellEditor() will create any of them automatically (including the editor for PsiClass), so that you won't need to think about which one to select every time.
Collection control is a complex control, so it's bound to a complex Swing component. It's called DomTableView (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/DomTableView.java). It has a toolbar (you can override DomTableView.getToolbarPosition() to customize its location), with Add and Delete buttons. If you want, you may specify custom addition actions in DomCollectionControl.createAdditionActions() (it's recommended to extend ControlAddAction). If there is only one addition action, it will be invoked after pressing the Add button; if there are many, then a popup menu will be displayed. To change the removal policy, override the DomCollectionControl.doRemove(List<T>) method.
The toolbar may also have an Edit button, if you specify that DomCollectionControl.isEditable(). To add a behavior to this button, override DomCollectionControl.doEdit(T). There can also be a Help button, if you pass a non-null String helpId parameter while constructing your DomTableView.
If there are no items in the collection, DomTableView may display a special text (DomTableView.getEmptyPaneText()), instead of an empty table.
You can add your own popup menu to the control. Call the DomTableView.installPopup() method after construction, and pass a DefaultActionGroup with your popup actions.
Tables can have single or multiple (default) row selection. If you want to change this behavior, override DomTableView.allowMultipleRowsSelection().

The easiest way to create a DOM-based UI form is to extend the BasicDomElementComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/BasicDomElementComponent.java) class. This will require you to pass some DOM element to the constructor. Then you bind an IntelliJ IDEA GUI Designer form to your subclass and design a beautiful form there. You will surely want to bind some controls to DOM UI, in which case you should of course ensure that they have the right types. Finally, you should create some DOM controls in class' constructor and bind them. But you can create controls and bind them to the DomElement's children - GenericDomValue's automatically.
Just name your components properly and call the bindProperties() method in the constructor. The field names should correspond to the getter names for the element's children. They may also be prefixed with "my". Imagine that you have such DOM interface:
public interface Converter extends DomElement {
GenericDomValue<String> getConverterId();
GenericDomValue<PsiClass> getConverterClass();
}In this case, the UI form class can look like this:
public class ConverterComponent extends BasicDomElementComponent<Converter> {
private JPanel myRootPane;
private TextPanel myConverterId;
private PsiClassPanel myConverterClass;
public ConverterComponent(Converter domElement) {
super(domElement);
bindProperties();
}
}All the fields here are now bound to the controls in the GUI form.
Very often, you'll have to create your own file editor. Then, to use all the binding and undo functionality, it's suggested to inherit your FileEditorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/openapi/fileEditor/FileEditorProvider.java) from PerspectiveFileEditorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/PerspectiveFileEditorProvider.java), create an instance of DomFileEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/DomFileEditor.java) there, and pass a BasicDomElementComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-openapi/src/com/intellij/util/xml/ui/BasicDomElementComponent.java). To easily create an editor with a caption at the top, like in our EJB and JSF, you may use the static method DomFileEditor.createDomFileEditor(). DomFileEditor automatically listens to all changes in the document corresponding to the given DOM element, and therefore refreshes your component on undo. If you want to listen to changes in additional documents, use the methods addWatchedDocument(), removeWatchedDocument(), addWatchedElement(), removeWatchedElement() in DomFileEditor.
The following bundled open-source plugins make (heavy) use of DOM:
Android (https://github.com/JetBrains/android)
Ant (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ant)
Plugin DevKit (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/devkit/devkit-core)
Maven (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven)
Explore 3rd party plugins using DOM on IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.dom.fileMetaData).
Code Completion (see Code Completion in Custom Languages (Code Completion))
Product Help: Postfix Completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#postfix_completion)
The Postfix Completion functionality allows developers to wrap a code fragment with a predefined template by typing a template abbreviation just after an expression meant to be wrapped, expanded, or modified. It relieves developers from typing repetitive or non-trivial code or helps to create the code faster, e.g., often it is convenient to write a code part and surround it with the required block without navigating the caret backward.
Consider a situation where a developer is not very familiar with a current Java project API and doesn't know how to name a variable that will be a result of an expression they want to type. The postfix completion makes it possible to write an expression at first and create a variable assignment with suggested names by adding a postfix template abbreviation at the end of the expression.
Assume that a user typed the following Java code:
void confirmOrder(Cart cart) {
cart.getDeliveryType().getDeliveryCost()<caret>
}To avoid moving the caret to the beginning of the line, the user can quickly create a variable assignment by adding the .var postfix abbreviation and expanding the template:
void confirmOrder(Cart cart) {
cart.getDeliveryType().getDeliveryCost().var<ENTER>
}When the template is applied, the above code is expanded to:
void confirmOrder(Cart cart) {
Money deliveryCost = cart.getDeliveryType().getDeliveryCost();<caret>
}In addition, the user can choose the best matching variable name from the name suggestions popup.
These sections describe how to implement Postfix Templates, and their associated building blocks, to plugins:
The IntelliJ Platform allows plugins to provide custom postfix templates specific to the supported languages, frameworks, or libraries.
To provide custom postfix templates for an existing or custom language, register an implementation of PostfixTemplateProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java) in the com.intellij.codeInsight.template.postfixTemplateProvider extension point (EP).
The PostfixTemplateProvider extension contains the list of templates that extend the PostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java) class. During the code completion mechanism, all postfix template providers registered for the current language are queried for their templates. All templates enabled and applicable in the current context will be added to the completion popup items set.
Examples:
JavaPostfixTemplateProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java) providing Java postfix templates
PyPostfixTemplateProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/codeInsight/postfix/PyPostfixTemplateProvider.java) providing Python postfix templates
The simplest way to create a postfix template is by extending the PostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java) class and implementing the key methods:
boolean isApplicable() determining whether the template can be used in the context described by parameters
void expand() inserting the template content in the editor
Examples:
InstanceofExpressionPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/InstanceofExpressionPostfixTemplate.java) surrounding an expression with the instanceof check
TryWithResourcesPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java) wrapping a selected expression in try-with-resources block
See the Advanced Postfix Templates section for information on how to implement postfix templates with more advanced features and editing possibilities.
All postfix templates must provide descriptions and examples showing the code before and after a template is expanded. The files describing the template must be placed in the plugin's resources in the postfixTemplates/$TEMPLATE_NAME$ where the $TEMPLATE_NAME$ directory must match the simple name of the template class, e.g., for a template implemented in com.example.IntroduceVariablePostfixTemplate class, the directory name should be named as IntroduceVariablePostfixTemplate.
Providing the description explaining the template purpose and context details is achieved by creating the description.html file.
Providing the code snippets showing the template in "before" and "after" expanding states is achieved via the before.$EXTENSION$.template and after.$EXTENSION$.template files accordingly. The $EXTENSION$ placeholder should be replaced with the extension of the template language, e.g., before.kt.template for a Kotlin template.
The code snippets included in the example files can use the <spot> marker, which should surround the most important code parts, e.g., expression to expand and position of the caret after expanding. Marked parts will be highlighted in the Settings | Editor | General | Postfix Completion settings page, making it easier for users to understand how a template is expanded, e.g.:
before.java.template:
<spot>cart.getProducts()</spot>.varafter.java.template:
List<Product> products = cart.getProducts();<spot></spot>Template example files can also use the $key placeholder which is replaced with the actual template key in the preview UI, e.g., consider a template with the var key:
The gutter icons for a postfix template class allow navigating to the corresponding description and before/after files in plugin resources.
Example: TryWithResourcesPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/postfixTemplates/TryWithResourcesPostfixTemplate) directory containing description files for TryWithResourcesPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/TryWithResourcesPostfixTemplate.java) template.
While simple templates (Postfix Templates) can be handled by extending PostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java) class directly, more advanced templates require additional functionalities like selecting the expression that a template should be applied to or editing a template content. The IntelliJ Platform provides the base classes simplifying advanced template's features implementation.
In some contexts, it is not obvious what expression a template should be applied to. Consider the following Java code with the var postfix template at the end:
order.calculateWeight() > getMaxWeight(order.getDeliveryType()).varIn the above code, a postfix template could be applied to the getMaxWeight() method invocation and the entire comparison expression depending on the user's intention. The postfix template implementation could automatically apply the template on the topmost or the closest applicable expression, but it is reasonable to leave the expression selection for the user.
Postfix templates with expression selector can be achieved by implementing PostfixTemplateWithExpressionSelector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateWithExpressionSelector.java) and PostfixTemplateExpressionSelector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateExpressionSelector.java) classes.
Example: IntroduceFieldPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/IntroduceFieldPostfixTemplate.java) introduces a Java class field, allowing to select one of the non-void expressions at the current offset.
The IntelliJ Platform-based IDEs provide the Live Templates (https://www.jetbrains.com/help/idea/using-live-templates.html) feature. It allows defining a template text with dynamic expressions, replaced with actual values depending on the context. If the implemented postfix template's expanding behavior can be achieved with the live template syntax, it is much easier to extend the StringBasedPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StringBasedPostfixTemplate.java) rather than implementing the expansion behavior programmatically.
Example: StreamPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/StreamPostfixTemplate.java) wraps array expression within the Arrays.stream() method.
See the Live Templates section in SDK Docs for information on implementing the live templates feature in a plugin.
All postfix templates that return true from the PostfixTemplate.isEditable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplate.java) method and have a key starting with . (dot) can be edited. In the simplest case, only the template's name can be edited. To provide more advanced editing possibilities, like creating new templates in the settings dialog, editing the template's content, variables, expression conditions, etc., a plugin must implement the related PostfixTemplateProvider's (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplateProvider.java) methods:
PostfixTemplateProvider.createEditor() - returns an instance of the PostfixTemplateEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/PostfixTemplateEditor.java) class which is responsible for building the UI settings for a particular template and creating a template from the settings provided in this UI. The editor UI may contain settings controls other than the textual editor, like lists, checkboxes, etc.
PostfixTemplateProvider.writeExternalTemplate() and readExternalTemplate() - serializes/deserializes a given template to/from XML elements. Serialized template data is stored in the PostfixTemplateStorage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplateStorage.java) persistent component (Persisting State of Components).
Implementing template, editor, and serialization methods from scratch is a tedious task, so the IntelliJ Platform provides classes that help implement editable templates:
EditablePostfixTemplateWithMultipleExpressions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/EditablePostfixTemplateWithMultipleExpressions.java) for editable templates with expression selector and conditions limiting the applicable contexts. Like , it is text-based and uses the same live template syntax.
PostfixTemplateEditorBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/settings/PostfixTemplateEditorBase.java) for editors implementing the most common features like editing content, expression conditions, or using the topmost expression for template expanding
Utility class PostfixTemplatesUtils (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixTemplatesUtils.java) providing methods related to editable template data serialization
Examples:
JavaPostfixTemplateProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/JavaPostfixTemplateProvider.java) providing Java editable templates
JavaPostfixTemplateEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/editable/JavaPostfixTemplateEditor.java) implementing editor for Java editable templates
ObjectsRequireNonNullPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/ObjectsRequireNonNullPostfixTemplate.java) implementing template wrapping the selected expression in Objects.requireNonNull() method
Existing Surrounder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java) implementations of the Surround With feature required to invoke the Code | Surround With... action can be reused for postfix completion by extending the SurroundPostfixTemplateBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java) class and returning the surrounder object from the getSurrounder() method.
Example: NotNullCheckPostfixTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/template/postfix/templates/NotNullCheckPostfixTemplate.java) surrounding the selected expression with an if statement checking if the expression is null.
Product Help: Live templates (https://www.jetbrains.com/help/idea/using-live-templates.html)
Live Templates are customizable rules that allow developers to abbreviate repetitive text patterns or surround code fragments with repetitive constructs in the editor.
When a user types the designated abbreviation followed by a configurable expansion key (usually Tab), the IDE transforms the preceding input sequence to its full-length output, and update the cursor position.
For example, consider a Java for loop. Typically, the end user would need to type for (int i = 0; i < 10; i++) {<Enter><Tab><Enter><Enter>}<Up>. This pattern may be shortened to fori<Tab> and the remaining contents will be expanded, leaving the following structure:
for (int i = [|]; i < []; i++) {
[]
}As the user completes each section of the for loop and presses Tab, the cursor advances to the next position in the editor.
Another use-case for live templates is surrounding the selected code with additional constructs. When a user selects a code fragment and invokes the Code | Surround With... action and chooses the template from the list, the code is wrapped with the content defined in the template.
Consider the following Java method with the selected fragment inside <selection>:
public void testMethod() {
<selection>getActions()</selection>
}Invoking the Code | Surround With... action and selecting the Iterate Iterable or array template would transform the code to:
public void testMethod() {
for (Action action : getActions()) {
<cursor>
}
}See the Surround With section for the information on how to implement more advanced code surrounding.
For more information about creating Custom Live Templates, refer to the corresponding documentation (https://www.jetbrains.com/idea/help/creating-and-editing-live-templates.html).
These sections describe how to add Live Templates, and their associated building blocks, to plugins.
Providing Live Templates (Providing Live Templates)
Creating New Functions for Live Templates (Creating New Functions for Live Templates)
This tutorial illustrates how to add default Custom Live Templates to an IntelliJ Platform plugin, and assign valid contexts for these templates based on the surrounding code and file type. In addition, the tutorial discusses how to export existing Live Templates, and bundle them within a plugin. Any Live Template that can be created and exported can be added to a plugin by following the Template Creation, Export, and Extension Point Registration processes.
This tutorial uses the SDK code sample live_templates (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/live_templates).
Get started by creating a new Live Template (https://www.jetbrains.com/idea/help/creating-and-editing-live-templates.html) within the IntelliJ Platform-based IDE:
Add a new Template Group, "Markdown" and create a new Live Template under this group.
Assign the template the abbreviation " { ".
Assign the description " SDK: New link reference ".
Paste the following snippet into the Template text field:
[$TEXT$]($LINK$)$END$The variables $TEXT$ and $LINK$ may be further configured in the Edit variables dialogue to reorder their precedence and bind to the functions that invoke auto-completion at the appropriate time. In the Edit variables dialog, set the Expression for the LINK to complete() using the combo box.
There are many predefined functions (https://www.jetbrains.com/help/idea/template-variables.html#predefined_functions) that developers should become familiar with before implementing any unique functionality in a plugin.
Consider iteratively testing the Live Template using the current editor and a markdown file to minimize debugging later.
Once the Live Template produces the expected result, export the Live Template (https://www.jetbrains.com/help/idea/sharing-live-templates.html). The export produces a file called Markdown.xml with the following contents:
<templateSet group="Markdown">
<template
name="{"
value="[$TEXT$]($LINK$)$END$"
description="SDK: New link reference"
toReformat="false"
toShortenFQNames="false">
<variable
name="TEXT"
expression=""
defaultValue=""
alwaysStopAt="true"/>
<variable
name="LINK"
expression="complete()"
defaultValue=""
alwaysStopAt="true"/>
</template>
</templateSet>The display description can also provide localized variants by specifying key and resource-bundle attributes instead (code insight is available in 2020.3 and later). A quick fix to extract the localized key is available since 2024.2.
Copy this file into the plugin's resources folder (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/live_templates/src/main/resources/liveTemplates).
A TemplateContextType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/template/TemplateContextType.java) tells the IntelliJ Platform where the Live Template is applicable: Markdown files. Every context must have a unique TemplateContextType defined for it, and the Platform defines many context types out of the box. The MarkdownContext class defines it for Markdown files. Ultimately, a file's extension determines the applicable Markdown context.
Once the MarkdownContext is defined, be sure to add the new context type to the previously created Live Template settings file.
Within the <template>...</template> elements in the Markdown.xml Live Template definition file, add the following context elements:
<template>
<variable ... />
<context>
<option name="MARKDOWN" value="true"/>
</context>
</template>It is not always necessary to define your own TemplateContextType, as there are many existing template contexts already defined in the IntelliJ Platform. Consider reusing one of the many existing template context types that inherit from TemplateContextType if you are augmenting language support to an existing area.
Depending on the version of the IntelliJ Platform, different steps are used to complete the implementation of the feature.
Using the com.intellij.defaultLiveTemplates and com.intellij.liveTemplateContext extension points, register the implementations with the IntelliJ Platform. The file attribute in com.intellij.defaultLiveTemplates EP specifies path/filename under the src/main/resources folder.
Specify required contextId attribute in com.intellij.liveTemplateContext EP instead of TemplateContextType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/template/TemplateContextType.java) constructor (existing declarations will continue working).
<extensions defaultExtensionNs="com.intellij">
<defaultLiveTemplates file="/liveTemplates/Markdown.xml"/>
<liveTemplateContext
contextId="MARKDOWN"
implementation="org.intellij.sdk.liveTemplates.MarkdownContext"/>
</extensions>Using the com.intellij.defaultLiveTemplates and com.intellij.liveTemplateContext extension points, register the implementations with the IntelliJ Platform. The file attribute in the com.intellij.defaultLiveTemplates EP specifies path/filename under the src/main/resources folder.
<extensions defaultExtensionNs="com.intellij">
<defaultLiveTemplates file="/liveTemplates/Markdown.xml"/>
<liveTemplateContext
implementation="org.intellij.sdk.liveTemplates.MarkdownContext"/>
</extensions>The MarkdownTemplateProvider implementing DefaultLiveTemplatesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/impl/DefaultLiveTemplatesProvider.java) tells the Platform where to find the Live Template settings file. Make sure to include the full path to the file, relative to the src/main/resources directory, excluding the file extension.
package org.intellij.sdk.liveTemplates;
import com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider;
import org.jetbrains.annotations.Nullable;
final class MarkdownTemplateProvider implements DefaultLiveTemplatesProvider {
@Override
public String[] getDefaultLiveTemplateFiles() {
return new String[]{"liveTemplates/Markdown"};
}
@Nullable
@Override
public String[] getHiddenLiveTemplateFiles() {
return null;
}
}Using the com.intellij.defaultLiveTemplatesProvider and com.intellij.liveTemplateContext extension points, register the implementations with the IntelliJ Platform.
<extensions defaultExtensionNs="com.intellij">
<defaultLiveTemplatesProvider
implementation="org.intellij.sdk.liveTemplates.MarkdownTemplateProvider"/>
<liveTemplateContext
implementation="org.intellij.sdk.liveTemplates.MarkdownContext"/>
</extensions>Now verify the plugin is working correctly. Run the plugin in a Development Instance and verify there is a new entry under Settings | Live Templates | Markdown | { (SDK: New link reference).
Finally, create a new file test.md and confirm that the Live Template works by entering a { character and then pressing Tab.
The Predefined Functions (https://www.jetbrains.com/help/idea/template-variables.html#predefined_functions) are the building blocks for creating Parameterized Templates and Surround Templates (https://www.jetbrains.com/help/idea/using-live-templates.html#live_templates_types). However, sometimes the Predefined Functions are not enough.
This tutorial illustrates how to add custom functions to an IntelliJ Platform plugin and make them available for use by Live Templates. As an example, a function is created to convert a selection to Title Case. Refer to the SDK code sample live_templates (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/live_templates).
Under the hood, the predefined functions for Live Templates are called macros. A new custom function for Live Templates is implemented in TitleCaseMacro, which extends MacroBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/template/macro/MacroBase.java). Three TitleCaseMacro methods are of particular interest:
The TitleCaseMacro() constructor passes the name and description of the macro to the parent constructor.
The isAcceptableInContext() method tests whether the macro is available in the current context. The test relies on the MarkdownContext ("Implement TemplateContextType" in "Providing Live Templates") object previously defined in the live_templates plugin.
The calculateResult() method gets invoked when the titleCase function is used in a Live Template. The text to be capitalized is retrieved from the Live Template and converted to Title Case.
Using the procedures previously discussed for Template Creation ("Template Creation" in "Providing Live Templates") and Export the Live Template ("Export the Live Template" in "Providing Live Templates"), add a Live Template to the Markdown.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/live_templates/src/main/resources/liveTemplates) file for the plugin. The XML representation of an example Live Template using the new titleCase function is listed below.
There is only one variable, TITLE. The expression for TITLE evaluates to the titleCase function provided by the plugin. The argument to the titleCase function is SELECTION, which tells the IntelliJ Platform to operate on the current selection.
<template
name="mc"
value="$TITLE$"
description="SDK: Convert to title case"
toReformat="true"
toShortenFQNames="false">
<variable
name="TITLE"
expression="titleCase(SELECTION)"
defaultValue="the quick brown fox"
alwaysStopAt="true"/>
<context>
<option name="MARKDOWN" value="true"/>
</context>
</template>Using the com.intellij.liveTemplateMacro extension point, register the implementation with the IntelliJ Platform.
<extensions defaultExtensionNs="com.intellij">
<liveTemplateMacro
implementation="org.intellij.sdk.liveTemplates.TitleCaseMacro"/>
</extensions>Now verify the plugin is working correctly.
Run the plugin in a Development Instance.
Create a new file testing.md and enter several words in lower case.
Highlight the text and enter invoke Code | Surround With... to open the Surround With popup. Confirm that the SDK: Convert to title case is available in the popup, and select it.
The selected text will change to have each word capitalized.
Product Help: File and Code Templates (https://www.jetbrains.com/help/idea/settings-file-and-code-templates.html), File templates (https://www.jetbrains.com/help/idea/using-file-and-code-templates.html), Templates with multiple files (https://www.jetbrains.com/help/idea/templates-with-multiple-files.html)
The File templates mechanism allows generating files and code fragments containing repetitive text and patterns. Its main purpose is to relieve users from unnecessary manual work by generating boilerplate code automatically.
File templates can be used to create new project files populated with predefined content like code scaffolds or license headers that are specific to a certain file type and context. For example, when a new Java class is created in IntelliJ IDEA, the file already contains a class declaration with the provided name and empty body. File Templates are not limited to creating a single file. It is possible to create templates with child/multiple files (https://www.jetbrains.com/help/idea/templates-with-multiple-files.html) used for a set of related files, e.g., model, view and controller classes, at once in an MVC framework.
Another use case is generating code fragments from code intentions and fixes, e.g., adding a test method inside an existing test class or a quick fix for adding a missing interface method implementation.
As file templates are based on Apache Velocity (https://velocity.apache.org/), their content is not static. A template can contain dynamic parts based on the context, e.g., project or package name, created entity name, author data, and more. It is also possible to create custom properties and populate them with the required values.
The contents of all listed file templates can be edited in the IDE settings so that users can adjust them to their specific needs.
These sections describe how to add File and Code Templates, and their associated building blocks, to plugins:
The IntelliJ Platform allows plugins to provide custom file templates specific to the plugin's functionalities. In order to include custom templates in the plugin, a template file has to be created and placed in the specific place of plugin resources, depending on the template's purpose.
There are multiple ways of creating file templates:
Use "File | Save File as Template…" action (https://www.jetbrains.com/help/idea/using-file-and-code-templates.html#save-file-as-template)
Create a new file template (https://www.jetbrains.com/help/idea/using-file-and-code-templates.html#create-new-template)
Copy an existing file template (https://www.jetbrains.com/help/idea/using-file-and-code-templates.html#copy-existing-template)
Once the file templates are created and saved, they should be copied to the plugin project's resources directory. The created template can be found in the IDE configuration directory (https://www.jetbrains.com/help/idea/directories-used-by-the-ide-to-store-settings-caches-plugins-and-logs.html#config-directory) in the fileTemplates directory, or they can be exported via File | Manage IDE Settings | Export Settings by selecting the File Templates (schemes) checkbox. The exported ZIP file will contain the fileTemplates directory with the created templates. The fileTemplates directory should be moved to the plugin project's resources folder, and the .ft extension must be added to the template files, e.g., My Class.java must be renamed to My Class.java.ft.
The template name and extension displayed in the IDE settings will automatically be extracted from the file name. The Enable Live Templates option will be enabled if the template's content includes a #[[$ fragment. Note that live templates (Live Templates) expressions should be surrounded with #[[ and ]]# tokens to avoid being parsed by the Apache Velocity template engine.
Please note that adding templates with child/multiple files in custom plugins is currently not supported.
File templates can be assigned to one of the predefined categories depending on their purpose and type.
The Files category contains templates used for creating new files. This is the main category, and it includes all templates placed directly in the fileTemplates directory. It should contain only templates for given language core entities, e.g., a class, an interface, or other items that users very frequently create. If the language defines a lot of core entities, see "Custom "Create File From Template" Actions" in "Using File Templates Programmatically" for a more user-friendly solution.
The Includes category contains reusable fragments used for including in other file templates using the Apache Velocity #parse() directive, e.g., license header or documentation comment skeleton. It includes templates located in the fileTemplates/includes directory.
The Code category contains templates used for inserting in existing files, e.g., a code fragment used in an intention action (Intentions) or quick fix ("Quick Fix Implementation" in "Code Inspections"). It includes templates located in the fileTemplates/code directory.
The Internal category contains templates that are not visible in the IDE settings by default and cannot be edited by users. It includes templates located in the fileTemplates/internal directory.
The Other category contains other templates organized in groups. It includes templates located in the fileTemplates/j2ee directory and registered via the com.intellij.fileTemplateGroup extension point (EP). Note that the j2ee directory name is historical and unrelated to the J2EE technology. This category is intended for templates that are not used for creating core language entities or are used less frequently by users, e.g., a specific XML configuration file, a framework-specific class in Java language, etc. To include file templates in the Other section of the Settings | Editor | File and Code Templates settings page, provide an implementation of the FileTemplateGroupDescriptorFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/fileTemplates/FileTemplateGroupDescriptorFactory.java) and register it via the com.intellij.fileTemplateGroup EP.
Example: MavenFileTemplateGroupFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/MavenFileTemplateGroupFactory.java)
Note that FileTemplateGroupDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/fileTemplates/FileTemplateGroupDescriptor.java) is a subclass of FileTemplateDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/fileTemplates/FileTemplateDescriptor.java), which allows creating nested groups.
By default, the description contains generic text about the syntax and properties of the given template. It is highly recommended to provide a custom description explaining its purpose and any available properties. Overriding the default description is achieved by creating an HTML file with the name matching template's name but with the .html extension. Example:
Template file name: My Class.java.ft
Description file name: My Class.java.html
The description file must be located in the same directory as the template file. It is recommended to follow the convention from the default.html (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources-en/src/fileTemplates/default.html) file.
A file template body can use a set of predefined properties (https://www.jetbrains.com/help/idea/file-template-variables.html#predefined_template_variables) exposed by the IntelliJ Platform out of the box. Some languages or frameworks may require additional properties exposed to existing templates. To provide custom properties, implement DefaultTemplatePropertiesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/ide/fileTemplates/DefaultTemplatePropertiesProvider.java) and register it via the com.intellij.defaultTemplatePropertiesProvider EP.
Example: Java Plugin's TemplatePackagePropertyProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/ide/fileTemplates/TemplatePackagePropertyProvider.java) providing PACKAGE_NAME property based on the directory a file is created in.
File templates provided by a plugin can be used during new file creation, in code intention actions, or other plugin features. They can be accessed with the FileTemplateManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core-impl/src/com/intellij/ide/fileTemplates/FileTemplateManager.java) service providing methods returning all or single file templates from a given category. For example, to obtain a template from the Code ("Code" in "Providing File and Code Templates") category, pass its name to the getCodeTemplate() method (notice the lack of the .ft extension):
FileTemplate template = FileTemplateManager.getInstance(project)
.getCodeTemplate("Test Class.java");To render a template content, prepare and pass Properties object to the getText() method:
Properties properties = new Properties();
properties.setProperty("PROP1", value1);
properties.setProperty("PROP2", value2);
String renderedText = template.getText(properties);The common use case for file templates is creating new files with the initial content specific to a language or framework supported by the plugin. File templates assigned to the Files ("Files" in "Providing File and Code Templates") category are automatically available in the File | New action group. Sometimes, creating a file from a given template in a specific project place doesn't make sense, or a template requires some additional properties for its content. It is possible to control a file template's visibility and its available properties using CreateFromTemplateHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/fileTemplates/CreateFromTemplateHandler.java) implementation registered in the com.intellij.createFromTemplateHandler EP.
Example: JavaCreateFromTemplateHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/ide/fileTemplates/JavaCreateFromTemplateHandler.java)
File templates from the Other ("Other" in "Providing File and Code Templates") category are not exposed by default. To make them available in the UI, a plugin has to implement and register an action. The easiest way to do it is by extending CreateFileFromTemplateAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java) base class which contains standard implementations of necessary methods. It allows customizing the action by providing an action name, icon, overriding methods creating dialog, and others. Example action:
public class CreateMyClassAction extends CreateFileFromTemplateAction {
@Override
protected void buildDialog(Project project, PsiDirectory directory,
CreateFileFromTemplateDialog.Builder builder) {
builder
.setTitle("New My File")
.addKind("Class", MyIcons.CLASS_ICON, "My Class");
}
@Override
protected String getActionName(PsiDirectory directory,
@NotNull String newName, String templateName) {
return "Create My Class: " + newName;
}
}The new action should be registered under the NewGroup group, e.g:
<actions>
<action id="Create.MyClass" class="com.example.CreateMyClassAction" icon="MyIcons.CLASS_ICON">
<add-to-group group-id="NewGroup"/>
</action>
</actions>Action presentation texts should be added to the resource bundle defined in plugin.xml ("resource-bundle" in "Plugin Configuration File") according to the rules described in "Localizing Actions and Groups" in "Actions":
action.Create.MyClass.text=My Class
action.Create.MyClass.description=Creates new classIn some cases, the default mechanism for creating files from templates is insufficient. Consider a language that defines multiple types of core entities, e.g., in the Java language, the following entities can be created: Class, Interface, Record, Enum, and Annotation.
Having all of those items in the File | New action group may overwhelm users with the number of options to choose. It is more user-friendly to provide a single File | New | Java Class action and let users choose a specific entity type in the creation dialog:

This can be achieved by placing templates in the Internal ("Internal" in "Providing File and Code Templates") category, so they are not picked up by the default mechanism, and then registering a custom CreateFileFromTemplateAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java) implementation as described in the section. To provide a list of selectable entity types, the action should provide a custom dialog with multiple file kinds for every template type, e.g.:
@Override
protected void buildDialog(Project project, PsiDirectory directory,
CreateFileFromTemplateDialog.Builder builder) {
builder
.setTitle("My File")
.addKind("Class", MyIcons.CLASS_ICON, "My Class")
.addKind("Record", MyIcons.RECORD_ICON, "My Record")
.addKind("Enum", MyIcons.ENUM_ICON, "My Enum");
}As file templates are placed in the fileTemplates/internal directory, they are not listed in the Settings | Editor | File and Code Templates settings page, and users can't adjust them to their needs. Internal templates can be exposed in the Files category by additionally registering them via the com.intellij.internalFileTemplate EP, e.g.:
<internalFileTemplate name="My Record"/>Example: NewKotlinFileAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt) for Kotlin files creation action.
Some languages or frameworks may require creating many custom file templates directly in the IDE with File | Save File as Template… action, including plugin developers creating templates, e.g., for Java language to support a specific framework. Adjusting created templates manually by replacing dynamic parts with properties can be tedious. It is possible to speed up this process by replacing known elements like package or class names with template properties placeholder. It can be achieved by implementing the SaveFileAsTemplateHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/actions/SaveFileAsTemplateHandler.java) and registering it via the com.intellij.saveFileAsTemplateHandler EP.
Example: SaveJavaAsTemplateHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/ide/fileTemplates/SaveJavaAsTemplateHandler.java) replacing existing class and package names with ${NAME} and ${PACKAGE_NAME} properties placeholders respectively.
Product Help: Quick Documentation (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation)
Quick Documentation helps users by showing documentation, e.g., for classes, functions, or methods inside the editor. Plugin authors implement DocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/DocumentationProvider.java) to show documentation for particular PSI elements (PSI Elements).
Implementations of DocumentationProvider can be registered either at the com.intellij.documentationProvider or the com.intellij.lang.documentationProvider extension point (EP). It is recommended to use the latter one when creating documentation that targets a specific language because providers registered as com.intellij.lang.documentationProvider will only be called for elements from that language. This is the reason they require the language attribute when registering the EP in the plugin.xml (Plugin Configuration File). The bigger picture here is that documentation providers co-exist and if there is more than one provider for the same element, the first one that returns a value different from null wins.
Although discouraged, the ordering of documentation providers can be influenced by using the order attribute when registering the extension. For instance, intellij.python.community.impl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/intellij.python.community.impl.xml) uses the following to call the external documentation provider before the default one (registered with id="pythonDocumentationProvider"):
<lang.documentationProvider
language="Python"
implementationClass="com.jetbrains.python.documentation.PythonExternalDocumentationProvider"
order="before pythonDocumentationProvider"/>For detailed instructions on how to implement a DocumentationProvider, please refer to the Custom Language Support (Documentation) section and the descriptions in the Custom Language Support Tutorial (20. Documentation).
Product Help: Intention Actions (https://www.jetbrains.com/help/idea/intention-actions.html)
Platform UI Guidelines: Inspections (https://jetbrains.design/intellij/text/inspections/)
This topic describes the conditional_operator_intention (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/conditional_operator_intention), a sample plugin that adds a new intention action (https://www.jetbrains.com/help/idea/intention-actions.html) to the IDE Intentions list. In addition, the sample plugin contains a JUnit-based test.
The IntelliJ Platform analyzes your code and helps handle situations that may result in errors. When a possible problem is suspected, the IDE suggests an appropriate intention action, denoted with special icons.
See Inspections (https://jetbrains.design/intellij/text/inspections/) topic in the IntelliJ Platform UI Guidelines on naming, writing description, and message texts for inspections/intentions.
You can view a list of all available intention actions as well as enable/disable them using the Intentions List (https://www.jetbrains.com/help/idea/intention-actions.html#intention-settings) in Settings | Editor | Intentions.
The conditional_operator_intention (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/conditional_operator_intention) sample plugin illustrates the use of the following techniques:
How to analyze a PSI tree (PSI Files).
How to find a Java token of interest in the PSI tree.
How to invoke a quick fix action for a token element under cursor using the PsiElementBaseIntentionAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java) class.
How to create a JUnit test for this plugin using the IdeaTestFixtureFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java) class.
In case of providing multiple intention actions for a single element, their ordering is indeterministic due to performance reasons. It is possible to push specific items up or down by implementing HighPriorityAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/HighPriorityAction.java) or LowPriorityAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/LowPriorityAction.java) respectively.
When launched, the sample plugin adds the SDK: Convert ternary operator to if statement item to the SDK intentions group in the Settings | Editor | Intentions.
See Code Samples (Code Samples) on how to set up and run the plugin.
The plugin analyzes symbols under the cursor in your code opened in the editor. If the cursor is positioned on the ? conditional operator, IntelliJ IDEA proposes to replace this conditional (ternary) operator with the "if-then-else" statement:

Invoking SDK: Convert ternary operator to if statement intention action will result in transforming expression to the form visible in the preview (Intention Action Preview) popup (code fragment on the right).
Please note that running the test requires setting system property idea.home.path in the test task configuration of the Gradle build script.
The sample plugin contains the ConditionalOperatorConverterTest Java class and the test data in the test/testData/ directory. To perform the plugin test, run the ConditionalOperatorConverterTest.testIntention() method.
Intention (Intentions) previews are supposed to work out-of-the-box in most cases. This page gives guidance to plugin authors in situations where this is not the case and to encourage thorough testing (Writing Tests) of intention actions.
Since version 2022.3, the IntelliJ Platform can show a preview for IntentionAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java) and LocalQuickFix (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java). That means when the user shows available context actions (https://www.jetbrains.com/help/idea/intention-actions.html#apply-intention-actions), an additional small popup highlights the differences between the current code and the code after executing the intention.
Under the hood, this preview is created using a copy of the current file inside a separate, headless editor where the intention is applied to get the result. The difference between this result and the original code is used to build the preview where changes that are about to happen are highlighted.
The IntelliJ Platform has a default implementation for the preview, which works out of the box when existing intentions fulfill specific requirements, make only localized changes to the code, and require no further user input.
However, in specific cases, plugin authors will need to adapt their intentions to prepare them for the correct display of the preview. In the following, plugin authors can learn the requirements for intention previews in detail and the alternative options for previews the IntelliJ Platform provides.
While this page focuses on the preview for IntentionAction, the generatePreview() method exists also for LocalQuickFix and the documentation and all discussed points are valid for both use cases.
The entry point for previews is generatePreview() available in IntentionAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java) and LocalQuickFix (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java). The default implementation will load the intention or quick fix and call its invoke() or applyFix() method on a copy of the original file. It returns an instance of IntentionPreviewInfo (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/preview/IntentionPreviewInfo.java). When the diff could be computed successfully it returns IntentionPreviewInfo.DIFF and when it was unsuccessful it returns IntentionPreviewInfo.EMPTY.
The preferred way for plugin authors is to keep using their intentions or quick fixes as is and only adapt the code if the default implementation of generatePreview() does not succeed. Further down, we will give detailed information about necessary code changes and requirements. However, sometimes it might not be viable to create the diff for the preview automatically. In such cases, plugin authors have several options to override generatePreview() and return a different IntentionPreviewInfo. The following options are available and described in the paragraphs below:
Return an instance of IntentionPreviewInfo.Html to show a customized message that is dynamically generated from the available information.
Return an instance of IntentionPreviewInfo.CustomDiff to provide an original and changed text that is used to show the diff.
Return IntentionPreviewInfo.EMPTY in cases where it is not possible to show any preview.
See IntentionPreviewUtils (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/preview/IntentionPreviewUtils.java) for a number of useful helper methods.
To show a dynamically generated HTML description as a preview, return an instance of IntentionPreviewInfo.Html from generatePreview(). Some important tips are:
You may use the HtmlChunk (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/text/HtmlChunk.java) API with it. If you want to display an icon, use HtmlChunk#icon but be careful because converting it to text and back (via HtmlChunk.raw) would remove the icon. Consider using HtmlChunk.template if necessary.
Note that the description pane has a fixed width of 300px for the default font, and you should try to make the description as concise as possible.
Any user interaction with the description pane is hardly possible and should not be relied upon. The first reason for that is that previews are usually displayed as a reaction to AltEnter by users who avoid using the mouse. Secondly, even with the mouse, it happens to hover over a different intention accidentally, which would close the current preview and make an interaction hard. Therefore, adding clickable links or buttons in previews is highly discouraged.
When possible, use generic preview methods available in IntentionPreviewInfo (e.g., rename(), navigate(), movePsi()). They provide a uniform preview for common cases.
For situations where the changes of an intention are not bound to the current file, a custom diff-like preview can be generated by returning IntentionPreviewInfo.CustomDiff from generatePreview(). The diff is then generated by comparing originalText with modifiedText. In many cases, originalText can be an empty string and then only the modifiedText will be displayed in the preview. However, crafting some originalText might be helpful if:
The diff should highlight the exact parts that were changed from originalText to modifiedText.
The code changed in several unrelated lines. The preview can show several parts without then unchanged lines in the middle and separate the parts nicely.
Finally, when returning a custom diff, plugin authors should specify a file name if possible.
If you use CodeInsightTestFixture (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java), please note the following useful methods:
checkPreviewAndLaunchAction() is a replacement for launchAction(). If you call launchAction() directly and your action should generate diff-preview which equals to original file content, use checkPreviewAndLaunchAction() with this call, and preview will be checked as well.
checkIntentionPreviewHtml() asserts that HTML preview is generated and that it equals to the supplied text.
getIntentionPreviewText() returns the preview text for a given action.
If you are unsatisfied with these methods, or you have a custom framework and want to support preview testing, please look through the implementations of the aforementioned methods.
If you use LightQuickFixTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixTestCase.java) or LightQuickFixParameterizedTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/LightQuickFixParameterizedTestCase.java) with the usual setup of having your test-input as beforeXyz and afterXyz, you can test previews as well. A special suffix -preview should be added to the comment line in the test-data to test preview. For example:
This explicitly allows for preview testing and checks that the text generated by preview on a non-physical file is the same as the text generated by actual action.
If you have a customized preview text or HTML, you may create a previewXyz file (preserve the original extension) near beforeXyz and afterXyz. Now, the preview (no matter if it is a diff or HTML) will be compared to the content of that file.
Finally, IntentionPreviewPopupUpdateProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewPopupUpdateProcessor.kt) provides useful helper functions.
To prepare existing intentions for the normal diff preview, plugin authors must understand the underlying framework and how it works to a certain degree. This section will provide these details and guide developers through possible scenarios.
Normally, a non-physical copy of the file is provided to the generatePreview() method. When overriding generatePreview(), you can apply any changes to that file and return IntentionPreviewInfo.DIFF. The diff between the original file content and the changed file content will be shown in the preview.
The default implementation of generatePreview() employs further checks to ensure your action is safe. These checks consist of the following:
Verify that startsInWriteAction() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java) returns true.
Verify that getElementToMakeWritable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/FileModifier.java) returns its argument.
Check if all instance fields in the class are either marked as @SafeFieldForPreview (defined in FileModifier (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/FileModifier.java)), their types are marked as @SafeTypeForPreview, or used types are known to be safe (primitives, strings, enums, classes, arrays of these, etc.) If this is the case, then the original invoke() or applyFix() method is called in a background thread on a non-physical copy of the file and with a mock editor (IntentionPreviewEditor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/preview/IntentionPreviewEditor.kt)).
If the above does not show a preview, there could be further problems of the following kind:
There are fields that are safe but have not been marked with @SafeFieldForPreview. Safe fields are ones which do not hold references to real PSI elements ("Do not Store PSI" in "Dynamic Plugins"), and if they do, these references are used for reading only.
There are fields that actually hold references to a physical PsiElement and they are modified by the action/fix. You can try to get rid of them by extracting the necessary PSI from the ProblemDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/ProblemDescriptor.java) or using PsiFile.getElementAt() with the editor caret position. Another option is to override getFileModifierForPreview(target). In this method, remap all the PSI elements to the target file (which is the copy of the source file) using PsiTreeUtil.findSameElementInCopy() and create a new instance of your action/fix. Example: DeleteMultiCatchFix.getFileModifierForPreview() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DeleteMultiCatchFix.java) Be careful, as there could be subclasses. Better play safe and declare your action/fix class as final.
invoke()/applyFix() starts a write action. If startsInWriteAction() returns true, then this is unnecessary, and you can remove the write action.
invoke()/applyFix() asserts (directly or indirectly) that it is invoked in the dispatch thread or inside a write action (see also General Threading Rules). The preview is applied in a background thread without a write action as it works on non-physical elements. You may consider removing these assertions or keeping them only if the preview is not active (use IntentionPreviewUtils.isIntentionPreviewActive()).
PsiDocumentManager.getDocument(psiFile) is used which isn't supported for a non-physical psiFile. Instead, use psiFile.getViewProvider().getDocument().
PsiDocumentManager.commitAllDocuments() is used. It’s unlikely that this call is required, and it probably slows down your action. You can commit the current document via PsiDocumentManager.commitDocument().
FileEditorManager.openTextEditor() or FileEditorManager.getSelectedEditor() is used to access the current editor (e.g., to start a template, position caret, add highlighting). Instead, use FileEditorManager.getSelectedTextEditor(), which should work in the preview and will point to a fake editor (where templates work). Also, you can branch on IntentionPreviewUtils.isIntentionPreviewActive() and avoid all editor-related operations because they won’t affect the preview anyway.
Application.invokeLater() is used. Currently, it’s prohibited in the preview and will fail with an exception. You may branch on IntentionPreviewUtils.isIntentionPreviewActive() and avoid doing this for preview, or you generate a custom preview as explained above. Also, note the tips in the paragraph below this list.
Non-trivial operations with the editor are used that are currently not implemented for the mock editor. We mock many operations but not all. E.g., getFoldingModel() is not currently supported. Avoid using these operations for non-physical files or for IntentionPreviewEditor.
Your action produces a side effect outside the current file. Examples include actions trying to change other PSI elements, changing the project or IDE settings, or launching an external process. In this case, startsInWriteAction() returning true and/or getElementToMakeWritable() returning its argument is incorrect. Override these methods properly and create a custom preview.
Your action uses non-physical elements for some purpose and branches on PsiElement.isPhysical() already, so in preview mode, this branch is wrongly taken. Instead, use IntentionPreviewUtils.isPreviewElement().
Your action modifies the results of ReferencesSearch.search(declaration), which always belongs to the original physical file, no matter if the passed declaration is a copy or an original. If you expect all the results to update belonging to the current file, you may traverse the file (PsiTreeUtil.processElements(), SyntaxTraverser.psiTraverser(), etc.) instead. Alternatively, remap the results of the search to the file copy using PsiTreeUtil.findSameElementInCopy(). If you need to update something outside the current file, a single file preview won’t work anyway. Either avoid updating the elements for the preview or create a custom preview.
If your action returns false from startsInWriteAction(), you should override generatePreview() and provide a custom diff or HTML preview. However, sometimes the default implementation of generatePreview() can still work. Here are two common scenarios:
Your action displays an additional popup or dialog but makes only changes to the current file after that. A solution here could be to display the preview assuming that the user selected default options in your UI and delegate to a method that is called after the UI is displayed, providing the default options.
Your action modifies a declaration in the current file and then updates all the call sites/overrides/references (probably involving slow search operation). One solution is to show only the modified declaration in the preview, ignoring all updated call sites or restricting them to the current file. Use IntentionPreviewUtils.isIntentionPreviewActive() to restrict the search scope when in preview.
Annotator
Inspections (Code Inspections)
Profiles
Scopes
Suppressing Highlights
Structural Search
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
The IntelliJ Platform provides a mechanism for analyzing the PSI tree and highlighting syntax errors out of the box.
While the PSI tree for the code is being built, a parser (Implementing Parser and PSI) tries to consume tokens according to language grammar. When it encounters a syntax error, like an unexpected token, a PsiErrorElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiErrorElement.java) is created and added to the PSI tree with an appropriate error description. In the code analysis daemon, the IDE visits every PSI element in the tree, and when a PsiErrorElement is encountered, information about it is collected and used while highlighting the code in the editor.
Additional highlighting can also be added using Annotators ("Syntax" in "Syntax and Error Highlighting") or Inspections, see Controlling Highlighting (Controlling Highlighting) on how to suppress.
In some cases highlighting syntax errors is insufficient or even unnecessary:
An error can be presented to the user in an easier-to-understand way.
The actual error cause is in a different location in the code, which is not easily visible when looking at the syntax error.
An error can be safely ignored in a given context, e.g., incomplete code fragment injected in a Markdown code block.
A syntax error is not critical and can be considered a warning or even information.
The IntelliJ Platform allows plugins to disable highlighting particular syntax errors. These errors can be optionally handled by additional Annotators or Inspections (Code Inspections) if needed.
To control which PsiErrorElements should be reported and which can be ignored, a plugin has to provide an implementation of HighlightErrorFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/highlighting/HighlightErrorFilter.java) and register it in the com.intellij.highlightErrorFilter extension point. It contains a single abstract method shouldHighlightErrorElement() which should return false if a given PsiErrorElement should not be highlighted in the editor.
Examples:
HtmlClosingTagErrorFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-analysis-impl/src/com/intellij/codeInsight/highlighting/HtmlClosingTagErrorFilter.java) ignoring unmatched closing tag in HTML files
MarkdownCodeFenceErrorHighlightingIntention (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/src/org/intellij/plugins/markdown/injection/MarkdownCodeFenceErrorHighlightingIntention.kt) ignoring all syntax errors in a code injected into a Markdown code blocks
See also:
Controlling Highlighting (Controlling Highlighting)
Product Help: Code Inspection (https://www.jetbrains.com/idea/webhelp/code-inspection.html), Creating Custom Inspections (https://www.jetbrains.com/idea/help/creating-custom-inspections.html)
Platform UI Guidelines: Inspections (https://jetbrains.design/intellij/text/inspections/)
The IntelliJ Platform provides tools designed for static code analysis called code inspections, which help the user maintain and clean up code without actually executing it. Custom code inspections can be implemented as IntelliJ Platform plugins. An example of the plugin approach is the comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) code sample.
See the Inspections (https://jetbrains.design/intellij/text/inspections/) topic in the IntelliJ Platform UI Guidelines on naming, writing description, and message texts for inspections.
The comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) code sample adds a new inspection to the Java | Probable Bugs group in the Inspections list (https://www.jetbrains.com/help/idea/inspections-settings.html). The inspection reports when the == or != operator is used between String expressions.
It illustrates the components for a custom inspection plugin:
Describing an inspection in the plugin configuration file.
Implementing a local inspection class to inspect Java code in the editor.
Creating a visitor to traverse the PSI tree of the Java file being edited, inspecting for problematic syntax.
Implementing a quick fix class to correct syntax problems by altering the PSI tree as needed. Quick fixes are displayed to the user like intentions (Intentions).
Writing an HTML description of the inspection for display in the inspection preferences panel.
Creating a test for the implemented inspection and quick fix.
Although the code sample illustrates implementations of these components, it is often useful to see examples of inspections implemented in the IntelliJ Community (https://github.com/JetBrains/intellij-community) code base. To identify a given inspection's implementation classes, try to find an inspection by name ("2.4 Search for Symbol Names" in "Explore the IntelliJ Platform API") or by UI texts ("2.5 Search by UI Text" in "Explore the IntelliJ Platform API"). Consider also searching for existing implementations in IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.localInspection).
The comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) code sample reports when the == or != operators are used between String expressions. The user can apply a quick fix to change a==b to a.equals(b), or a!=b to !a.equals(b).
The details of the comparing_string_references_inspection implementation illustrate the components of an inspection plugin.
The comparing_string_references_inspection is described as a com.intellij.localInspection extension point in the comparing_string_references_inspection plugin configuration (plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection/src/main/resources/META-INF/plugin.xml)) file.
There exist two types of inspection extensions:
The com.intellij.localInspection extension point is used for inspections that operate on one file at a time, and also operate "on-the-fly" as the user edits the file.
The com.intellij.globalInspection extension point is used for inspections that operate across multiple files, and the associated fix might, for example, refactor code between files.
The minimum inspection setup must declare the implementationClass and language attribute (unless the inspection works on any supported language). As shown in the comparing_string_references_inspection plugin configuration file, other attributes can be defined in the localInspection element, either with or without localization. In most cases, it is simplest to define the attributes in the plugin configuration file because the underlying parent classes handle most of the class responsibilities based on the configuration file description.
If required, inspections can define all the attribute information (except implementationClass) by overriding methods in the inspection implementation class (not recommended in general).
Inspection implementations for Java files, like ComparingStringReferencesInspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection/src/main/java/org/intellij/sdk/codeInspection/ComparingStringReferencesInspection.java), are often based on the Java class AbstractBaseJavaLocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java). The AbstractBaseJavaLocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java) base class offers methods to inspect Java classes, fields, and methods.
More generally, localInspection types are based on the class LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java). Examining the class hierarchy for LocalInspectionTool shows that the IntelliJ Platform provides many child inspection classes for a variety of languages and frameworks. One of these classes is a good basis for a new inspection implementation, but a bespoke implementation can also be based directly on LocalInspectionTool.
The primary responsibilities of the inspection implementation class are to provide:
A PsiElementVisitor object to traverse the PSI tree of the file being inspected.
A LocalQuickFix class to fix an identified problem (optional).
An options panel to be displayed in the Inspections settings dialog (optional). See Inspection Options for more details.
The overridden ComparingStringReferencesInspection methods are discussed in the sections below.
The visitor class evaluates whether elements of the file's PSI tree are of interest to an inspection.
The ComparingStringReferencesInspection.buildVisitor() method creates an anonymous visitor class based on JavaElementVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java) to traverse the PSI tree of the Java file being edited, inspecting for suspect syntax. The anonymous class overrides visitBinaryExpression(), which checks if a PsiBinaryExpression's operator is == or !=, and if both operand types are String.
The quick fix class acts much like an intention, allowing the user to invoke it on the PsiElement (or TextRange) highlighted by the inspection.
The ComparingStringReferencesInspection implementation uses the nested class ReplaceWithEqualsQuickFix to implement a quick fix based on LocalQuickFix (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java). The ReplaceWithEqualsQuickFix class allows the user to change the use of a == b and a != b expression to a.equals(b) and !a.equals(b) respectively.
The heavy lifting is done in ReplaceWithEqualsQuickFix.applyFix(), which manipulates the PSI tree to convert the expressions. The change to the PSI tree is accomplished by the usual approach to modification:
Getting a PsiElementFactory.
Creating a new PsiMethodCallExpression.
Substituting the original left and right operands into the new PsiMethodCallExpression.
Replacing the original binary expression with the PsiMethodCallExpression.
In case of providing multiple quick fixes for a single element, their ordering is indeterministic due to performance reasons. It is possible to push specific items up or down by implementing HighPriorityAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/HighPriorityAction.java) or LowPriorityAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/LowPriorityAction.java) respectively.
The inspection description is an HTML file. The description is displayed in the upper right panel of the Inspections settings dialog when an inspection is selected from the list.
See the Inspections (https://jetbrains.design/intellij/text/inspections/) topic in the IntelliJ Platform UI Guidelines on important guidelines for writing the description.
Implicit in using LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java) in the class hierarchy of the inspection implementation means following some conventions.
The inspection description file is expected to be located under $RESOURCES_ROOT_DIRECTORY$/inspectionDescriptions/. If the inspection description file is to be located elsewhere, override getDescriptionUrl() in the inspection implementation class.
The name of the description file is expected to be the inspection $SHORT_NAME$.html as provided by the inspection description, or the inspection implementation class. If a short name is not provided, the IntelliJ Platform computes one by removing Inspection suffix from the implementation class name.
(2023.2)
Using the following HTML structure, the description can embed code snippets that will be displayed with syntax highlighting:
<p>
The following code will be shown with syntax highlighting:
</p>
<pre>
<code>
// code snippet
</code>
</pre>The language will be set according to the inspection's registration language attribute. If required (e.g., when targeting UAST (UAST - Unified Abstract Syntax Tree)), it can be specified explicitly via <code lang="LanguageID">...</code>.
To open related settings (Settings) directly from the inspection description, add a link with settings://$CONFIGURABLE_ID$, optionally followed by ?$SEARCH_STRING$ to pre-select UI element:
See <em>Includes</em> tab in <a href="settings://fileTemplates">Settings | Editor | File and Code Templates</a> to configure.Please note that running the test requires setting system property idea.home.path in the test task configuration of the Gradle build script.
The comparing_string_references_inspection code sample provides a test for the inspection. See the Testing Overview section for general information about plugin testing.
The comparing_string_references_inspection test is based on the UsefulTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java) class, part of the JUnit framework APIs. This class handles much of the underlying boilerplate for tests.
By convention, the folder test/testData/ contains the test files. The folder contains pairs of files for each test using the name convention ∗.java and ∗.after.java, e.g., Eq.java/Eq.after.java.
The comparing_string_references_inspection tests run the inspection on the ∗.java files, apply the quick fix, and compare the results with the respective ∗.after.java files containing expected results.
The comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) code sample adds a new inspection to the Java | Probable Bugs group in the Inspections (https://www.jetbrains.com/help/idea/inspections-settings.html) configuration.
See Code Samples on how to set up and run the plugin.
The plugin inspects your code opened in the IntelliJ IDEA editor. The plugin highlights the code fragments where two String expressions are compared by == or != and proposes to replace this code fragment with .equals():

In this example, the str1 and str2 are variables of the String type. Invoking SDK: Use equals() will result in transforming expression to the form visible in the preview (Intention Action Preview) popup (code fragment on the right).
Some code inspections provide configuration options that affect their behavior. For example, Java | Code style issues | 'size() == 0' can be replaced with isEmpty(), allows ignoring classes from the defined list or expressions, which would be replaced with !isEmpty().
Currently, there are two ways of providing the inspection options:
Declarative
UI-based
Declarative inspection options API is available since version 2023.1.
Declarative API allows to:
delegate component rendering to the platform and make all the inspection options UI consistent and compliant with the IntelliJ Platform UI Guideline (https://jetbrains.design/intellij/)
optimize checking whether the inspection contains any options
manipulate options in places other than inspection panels (e.g., in quick fixes)
render options in contexts other than IntelliJ Platform-based IDEs
Providing the inspection options is achieved by implementing InspectionProfileEntry.getOptionsPane() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java), which returns an OptPane (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/options/OptPane.java) object describing available configuration possibilities. Note that InspectionProfileEntry is a parent of inspection base classes like LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java) and GlobalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java).
Building the inspection options is achieved by using a DSL-like facade, which contains methods for creating option controls and binding them to the fields declared in an inspection class.
Building the options for Java | Code style issues | 'size() == 0' can be replaced with 'isEmpty()' is implemented as follows:
public OrderedSet<String> ignoredTypes = new OrderedSet<>();
public boolean ignoreNegations = false;
@Override
public @NotNull OptPane getOptionsPane() {
return pane(
stringList(
"ignoredTypes",
message("options.label.ignored.classes"),
new JavaClassValidator()),
checkbox(
"ignoreNegations",
message("size.replaceable.by.isempty.negation.ignore"))
);
}The above example builds a form with two options (see SizeReplaceableByIsEmptyInspection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspection.java) for the full implementation context):
List of strings, which are validated for being Java classes. The provided list is bound to the ignoredTypes field in the inspection class.
Checkbox, which value is bound to the boolean ignoreNegations field in the inspection class.
The OptPane class exposes methods for building fields of other types, e.g., number or dropdown fields.
Note that the bind identifiers passed as a first string argument of methods creating form controls contain injected references that resolve to the bound fields. It enables resolving and other resolve-related features available, making it easy to rename fields and minimizing the risk of introducing typos resulting in bugs, as unresolved references will be highlighted as errors.
The default way of binding option form values to fields may be insufficient in more advanced cases. It is possible to customize the way of binding options by providing a custom OptionController (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/options/OptionController.java) from InspectionProfileEntry.getOptionController() method.
Consider the Properties files | Inconsistent resource bundle global inspection, from bundled Properties plugin in IntelliJ IDEA, which reports several types of inconsistencies in .properties files. The inspection allows enabling or disabling reporting specific issue types, which are reported by providers implementing a dedicated interface. Information about enabled providers is stored in a map where the key is a provider ID. The options panel and value binding are implemented in the following way (see InconsistentResourceBundleInspection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java) for the full implementation context):
private NotNullLazyValue<InconsistentResourceBundleInspectionProvider[]> myProviders = ...;
private Map<String, Boolean> mySettings = new LinkedHashMap<>();
@Override
public @NotNull OptPane getOptionsPane() {
return new OptPane(ContainerUtil.map(
myProviders.getValue(),
provider -> checkbox(provider.getName(), provider.getPresentableName())));
}
@Override
public @NotNull OptionController getOptionController() {
return OptionController.of(
(bindId) -> ContainerUtil.getOrElse(mySettings, bindId, true),
(bindId, value) -> {
boolean boolValue = (Boolean)value;
if (boolValue) {
mySettings.remove(bindId);
} else {
mySettings.put(bindId, false);
}
});
}Option controls panel is built based on providers’ IDs and presentable names. This implementation doesn't need to be changed regardless of removing or adding new providers in the future.
Reading and writing options in the map is achieved by registering a custom controller with getter and setter logic provided to the OptionController.of() method.
It's possible to compose several option controllers into the hierarchy based on the bindId prefix. It may be useful when some inspections have common configuration options and store the configuration in dedicated objects. See OptComponent.prefix() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/options/OptComponent.java) and OptionController.onPrefix() methods for more details and example implementation: MissingJavadocInspection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInspection/javaDoc/MissingJavadocInspection.java).
Sometimes, inspections use options that are rendered in a non-standard way, or are shared with other inspections or other IDE features. Such a shared configuration can be implemented as a persistent component (Persisting State of Components) and not have a single owner. It is still convenient to be able to configure these options from the inspection panel.
An example of such a case is the Java | Probable bugs | Nullability problems | @NotNull/@Nullable problems inspection, which contains the Configure Annotations… button that opens the Nullable/NotNull Configuration dialog.
Custom Swing controls can be provided by implementing CustomComponentExtensionWithSwingRenderer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInspection/ui/CustomComponentExtensionWithSwingRenderer.java) and registering the implementation in the com.intellij.inspectionCustomComponent extension point. Please note that this API is still in experimental state and may be changed without preserving backward compatibility.
Example: JavaInspectionButtons (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/options/JavaInspectionButtons.java) providing buttons for configuring options in custom dialogs
If you target versions 2023.1+ only, it is highly recommended to implement .
UI-based inspection options are provided by implementing a configuration panel using Swing components and returning it from InspectionProfileEntry.createOptionsPanel() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java) method. It returns the panel with option components that bind the provided values to the inspection class fields or other properties, similarly as in the declarative approach. Note that since version 2023.1, this method is ignored if InspectionProfileEntry.getOptionPane() returns a non-empty panel.
Example: SizeReplaceableByIsEmptyInspection (https://github.com/JetBrains/intellij-community/blob/223/plugins/InspectionGadgets/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspection.java) in version 2022.3, implemented using the UI-approach
For simple customization requirements, see also:
SingleCheckboxOptionsPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInspection/ui/SingleCheckboxOptionsPanel.java) for single checkbox
MultipleCheckboxOptionsPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInspection/ui/MultipleCheckboxOptionsPanel.java) for multiple checkboxes
SingleIntegerFieldOptionsPanel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInspection/ui/SingleIntegerFieldOptionsPanel.java) for single Integer (text field)
Be careful when you have a hierarchy of inspection classes. For example, if inspection superclass is converted to the declarative approach, any createOptionsPanel() methods in subclasses will be ignored. If you can't convert all of them at once, you may temporarily add getOptionsPane() returning OptPane.EMPTY to subclasses, where createOptionsPanel() is still used.
The results of analyzing code by several mechanisms provided by the IntelliJ Platform (Syntax errors (Syntax Errors), Annotators, Inspections (Code Inspections)) are converted to highlighting information used to highlight the code in the editor. However, in some contexts, the provided highlighting information is invalid or unnecessary.
Consider a tool that allows changing Java language's syntax by implicitly generating getters and setters for annotated fields during the build so that they can be omitted in class implementation:
class Person {
@GetterSetter
private int age;
}
// usage:
person.setAge(47); // valid at runtimeJava support in IntelliJ IDEA would report the above setter usage as an unresolved code symbol. The resulting error annotation would be valid from the Java language point of view but invalid in a project using such a tool.
Another case where highlighting code issues is unnecessary is old file revisions from VCS. For example, the old version of a file could be created in a different project context, with other libraries configured. If the old file version used the library that is not used by the project currently, it would cause reporting false-positive code issues.
The IntelliJ Platform exposes the extension point allowing a plugin to decide which highlighting information will be visible in the editor. To do that, a plugin has to provide an implementation of HighlightInfoFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/HighlightInfoFilter.java) and register it in the com.intellij.daemon.highlightInfoFilter extension point. It contains a single method accept(), which should return true if a given HighlightInfo should be visible in the editor and false to ignore it.
Examples:
DebuggerHighlightFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DebuggerHighlightFilter.java) disabling reporting unhandled exceptions in the debugger code editor
LombokHighlightErrorFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/lombok/src/main/java/de/plushnikov/intellij/plugin/extension/LombokHighlightErrorFilter.java) disabling false-positive error reports in a project using Lombok
See also:
Controlling Syntax Errors Highlighting ("Controlling Syntax Errors Highlighting" in "Syntax Errors")
Product Help: Project tool window (https://www.jetbrains.com/help/idea/project-tool-window.html)
Plugin authors can modify the representation of nodes in the project view. This is used to, e.g., change the icon of module nodes to reflect the module type or add URL and server path to Python Jupyter directories as location strings.
To modify project view node representations, implement ProjectViewNodeDecorator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/projectView/ProjectViewNodeDecorator.java) and register it as com.intellij.projectViewNodeDecorator extension. From the interface only the decorate() method that modifies ProjectViewNodes needs to be implemented. If you need to update your node representation on certain events, please use ProjectView.refresh() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/projectView/ProjectView.java).
This tutorial is meant to illustrate how the project tree structure view appearance can be modified programmatically.
This topic describes the treeStructureProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/tree_structure_provider) sample plugin.
The steps below show how to filter out and keep visible only text files and directories in the Project View Panel.
Other use cases include:
grouping/nesting related entries, e.g., GUI Designer (https://www.jetbrains.com/help/idea/gui-designer-basics.html) .form file and related bound class (FormMergerTreeStructureProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMergerTreeStructureProvider.java)).
provide additional "nested" nodes, e.g., contents of custom archive file
Add new com.intellij.treeStructureProvider extension to the plugin.xml (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/tree_structure_provider/src/main/resources/META-INF/plugin.xml)
<extensions defaultExtensionNs="com.intellij">
<treeStructureProvider implementation="org.intellij.sdk.treeStructureProvider.TextOnlyTreeStructureProvider"/>
</extensions>To provide custom Structure View behaviour, implement TreeStructureProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/projectView/TreeStructureProvider.java) with the nodes filtering logic in modify() method. If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
The example below shows how to filter out all the Project View nodes except those which correspond to text files and directories.
See Code Samples (Code Samples) on how to set up and run the plugin.
After going through the steps described above, you can see only text files and directories belonging to a project in the Project View.

Adding JPS support to your plugin requires Java plugin to be present for it to work. Please see Plugin Dependencies (Plugin Dependencies) on how to set up your plugin with required dependency.
When the user invokes an action that involves executing an external build (Make, Build Artifacts, etc.), the following steps happen:
Before-compile tasks are performed in the IDE process.
Some source generation tasks that depend on the PSI (e.g., UI designer form to source compilation) are executed in the IDE process.
BuildTargetScopeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/compiler/impl/src/com/intellij/compiler/impl/BuildTargetScopeProvider.java) extensions are called to calculate the scope of the external build (the set of build targets to compile based on the target module to make and the known set of changes).
The external build process is spawned (or an existing build process background process is reused).
The external build process loads the project model (.idea, .iml files, and so on), represented by a JpsModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/model-api/src/org/jetbrains/jps/model/JpsModel.java) instance.
The full tree of targets to build is calculated based on each build target's dependencies to be compiled.
For each target, the set of builders capable of building this target is calculated.
For every target and every builder, the build() method is called. This can happen in parallel if the "Compile independent modules in parallel" option is enabled in the settings. For module-level builders, the order of invoking builders for a single target is determined by their category; for other builders, the order is undefined.
Caches to record the state of the compilation are saved.
Compilation messages reported through the CompileContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java) API are transmitted to the IDE process and displayed in the UI (in the Messages view).
Post-compile tasks are executed in the IDE process.
To support incremental build, the build process uses several caches which are persisted between build invocations. Even if your compiler doesn't support incremental build, you still need to report correct information so that incremental build works correctly for other compilers.
SourceToOutputMapping (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/builders/storage/SourceToOutputMapping.java) is a many-to-many relationship between source files and output files ("which source files were used to produce the specified output file"). It's filled by calls to BuildOutputConsumer.registerOutputFile() and ModuleLevelBuilder.OutputConsumer.registerOutputFile().
The IDE monitors the project content changes and uses the information from those caches to generate the set of dirty and deleted files for every compilation. (Dirty files need to be recompiled, and deleted files need to have their output deleted). A builder can also report additional files as dirty (e.g., if a method is deleted, the builder can report the classes using this method as dirty.) A module-level builder can add some files to the dirty scope; if this happens, and if the builder returns ADDITIONAL_PASS_REQUIRED from its build() method, another round of builder execution for the same module chunk will be started with the new dirty scope.
A builder may also want to have its custom caches to store additional information to support the partial recompilation of a target (e.g., the dependencies between Java files in a module). To store this data, you can either store arbitrary files in the directory returned from BuildDataManager.getDataPaths().getTargetDataRoot() or use a higher-level API: BuildDataManager.getStorage()
To pass custom data between the invocation of the same builder between multiple targets, you can use CompileContext.getUserData() and CompileContext.putUserData().
The external builder process uses the standard Java services (https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism to support plugins. There are several service interfaces (e.g. BuilderService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java) which can be implemented in plugins to extend the builder functionality. An implementation of a service needs to be registered by creating the META-INF/services/$service-interface-fqn$ file containing the implementation class's qualified name. E.g. BuilderService implementations are registered in META-INF/services/org.jetbrains.jps.incremental.BuilderService file. These files don't have extensions, so you need to map corresponding patterns to text files in IDE settings.
Sources of a plugin for External Builder should be put in a separate module. By convention, such a module has a name '...-jps-plugin', and its sources are placed under the jps-plugin directory in the main plugin directory. Use com.intellij.compileServer.plugin extension point to add the plugin to the classpath of the external build process, the plugin JAR should be named $JPS_module_name$.jar. Build | Prepare Plugin Module for Deployment action will automatically pack the 'jps-plugin' part to a separate JAR accordingly.
See IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.compileServer.plugin) for samples.
If your test IDE is IntelliJ IDEA 16.0 or newer
Switch on "Debug Build Process" toggle action (available via 'Find Action') in the test IDE. After that, every time compilation is run, the build process will wait for debugger connection on some (random) port and will show the port number in the status bar. In a working copy of IDE, a "Remote" run configuration should be created and pointed to this port. Suppose you often need to debug external builders and want to reuse the created "Remote" run configuration. In that case, you may fix the port number by adding the following VM option to the plugin run configuration:
-Dcompiler.process.debug.port=<port-number>If your test IDE is IntelliJ IDEA 15.0 or older
Start IDE with your plugin with the following VM option:
-Dcompiler.process.debug.port=<port-number>After that, every time compilation is run in the test IDE, the build process will wait for debugger connection on this port and then proceed. In a working copy of IDE, a "Remote" run configuration should be created and pointed to this port. Specifying port "-1" will disable debugging mode.
The build process has built-in self-CPU-profiling capabilities.
2023.2+ Install YourKit Profiler for IDE Performance Testing (https://plugins.jetbrains.com/plugin/20892-yourkit-profiler-for-ide-performance-testing) plugin
Copy $IDE_HOME$/lib/yjp-controller-api-redist.jar and $IDE_HOME$/bin/yjpagent.* files to $IDE_SYSTEM_DIR$/compile-server
In Settings | Build, Execution, Deployment | Compiler | Java Compiler add -Dprofiling.mode=true in Additional command line parameters
Make sure Build project automatically in Settings | Build, Execution, Deployment | Compiler is disabled
After this, every build process run should result in a CPU snapshot stored in $USER_HOME$/Snapshots directory. Snapshots are named like ExternalBuild\-\$DATE$.snapshot.
Specifying -Dprofiling.mode=false will turn profiling off. Please capture a couple of snapshots for the situations you believe the build should work much faster than it does.
Please create an issue in the issue tracker and attach generated *.snapshot files to it or upload them as described here (https://intellij-support.jetbrains.com/hc/en-us/articles/206869619) and specify links in the issue. Please also provide details about the memory and other VM settings for the build process you were using.
The log file is located under the directory:
<ide-system-directory>/log/build-logThere, both build-log.log and build-log.properties files can be found. The build-log.properties is a log4j configuration file, where the log level and desired logging categories can be adjusted. This file contains logging from all build sessions, including those from the auto-make.
In IntelliJ Platform versions before version 14.1, log4j configuration was stored in build-log.xml.
The project model in the External Build process is provided by JPS (JetBrains Project System). A project is represented by JpsProject (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/model-api/src/org/jetbrains/jps/model/JpsProject.java), a module by JpsModule (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/model-api/src/org/jetbrains/jps/model/JpsProject.java), and so on. Suppose your compiler depends on something that isn't added to the model yet (e.g., some facet settings). In that case, you need to extend the JPS model (use JpsOsmorcModuleExtension as a reference implementation) and provide an implementation of JpsModelSerializerExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/model-serialization/src/org/jetbrains/jps/model/serialization/JpsModelSerializerExtension.java) to load the configuration from project files.
If your compiler isn't involved in the compilation of an existing BuildTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java), you need to create a new implementation of BuildTarget and BuildTargetType. Also, register an implementation of BuildTargetScopeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/compiler/impl/src/com/intellij/compiler/impl/BuildTargetScopeProvider.java) extension on the IDE side to add required targets to the build scope. The builder implementation should extend either TargetBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/incremental/TargetBuilder.java) or ModuleLevelBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java) class and should be created using BuilderService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java) extension.
Most of the tests in the IntelliJ Platform codebase are model-level functional tests. What this means is the following:
The tests run in a headless environment that uses real production implementations for most components, except for many UI components.
The tests usually test a feature as a whole rather than individual functions that comprise its implementation.
The tests do not test the Swing UI and work directly with the underlying model instead (see also ).
Most tests take a source file or a set of source files as input data (Test Project and Testdata Directories), execute a feature, and compare the output with expected results. Results can be specified as another set of source files, special markup (Testing Highlighting) in the input file, or directly in the test code.
The most significant benefit of this test approach is that tests are very stable and require very little maintenance once written, no matter how much the underlying implementation is refactored or rewritten.
In a product with 20+ years of a lifetime that has gone through many internal refactorings, we find that this benefit dramatically outweighs the downsides of slower test execution and more difficult debugging of failures compared to more isolated unit tests.
Another consequence of our testing approach is that we do not provide a recommended approach to mocking. We have a few tests in our codebase that use JMock. Still, in general, we find it difficult to mock all the interactions with IntelliJ Platform components that your plugin class will need to have. We recommend working with real components instead. See also "How to replace component/service in tests?" in "Testing FAQ" and "How to replace extension points in tests?" in "Testing FAQ".
Please see the dedicated intellij-ui-test-robot (https://github.com/JetBrains/intellij-ui-test-robot) library. It is fully integrated with Gradle-based setup via runIdeForUiTests ("runIdeForUiTests" in "Gradle IntelliJ Plugin") task.
Please do not use platform/testGuiFramework, as it is reserved for internal use.
Check out this step-by-step tutorial (Testing a Custom Language Plugin) teaching how to write and run automated tests for your custom language plugin. Also, code samples comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) and conditional_operator_intention (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/conditional_operator_intention) demonstrate using tests.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
The IntelliJ Platform testing infrastructure is not tied to any specific test framework. In fact, the IntelliJ IDEA Team uses JUnit (https://junit.org), TestNG (https://testng.org), and Cucumber (https://cucumber.io/) for testing different parts of the project. However, most of the tests are written using JUnit 3.
When writing your tests, you have the choice between using a standard base class to perform the test set up for you and using a fixture class, which lets you perform the setup manually and does not tie you to a specific test framework.
With the former approach, you can use classes such as BasePlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/BasePlatformTestCase.java) (LightPlatformCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java) before 2019.2).
With the latter approach, you use the IdeaTestFixtureFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java) class to create instances of fixtures for the test environment. You need to call the fixture creation and setup methods from the test setup method used by your test framework.
Plugin tests run in a real, rather than mocked, IntelliJ Platform environment and use real implementations for most application and project services (Services).
Loading and initializing all the project components and services for a project to run tests is a relatively expensive operation, and it is desired to avoid doing it for each test. Dependently on the loading and execution time, we make a difference between light tests and heavy tests available in the IntelliJ Platform test framework:
Light tests reuse a project from the previous test run when possible.
Heavy tests create a new project for each test.
Light and heavy tests use different base classes or fixture classes, as described below.
Because of the performance difference, we recommend plugin developers to write light tests whenever possible.
The standard way of writing a light test is to extend one of the following classes:
Use LightPlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java) or BasePlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/BasePlatformTestCase.java) for tests that don't have any dependency on Java functionality.
For 2019.2 and earlier, use LightPlatformCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/LightPlatformCodeInsightFixtureTestCase.java).
Examples:
JavaCopyrightTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-tests/testSrc/com/intellij/copyright/JavaCopyrightTest.kt)
HtmlDocumentationTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/tests/src/com/intellij/html/HtmlDocumentationTest.java)
AcceptWordAsCorrectTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/testSrc/com/intellij/spellchecker/inspector/AcceptWordAsCorrectTest.java)
For tests that require the Java PSI ("Java" in "IntelliJ IDEA Plugin Development") or related functionality:
LightJavaCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase.java) for JUnit 3
LightJavaCodeInsightFixtureTestCase4 (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase4.kt) for JUnit 4 (2021.1 and later)
LightJavaCodeInsightFixtureTestCase5 (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase5.kt) for JUnit 5 (2021.1 and later)
For 2019.2 and earlier, use LightCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightCodeInsightFixtureTestCase.java).
Examples:
PatternValidatorTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/IntelliLang-tests/test/org/intellij/plugins/intelliLang/pattern/PatternValidatorTest.java) (JUnit 3)
JavaCtrlMouseTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-tests/testSrc/com/intellij/java/codeInsight/javadoc/JavaCtrlMouseTest.kt) (JUnit 4)
MissingJavadocHighlightingTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/MissingJavadocHighlightingTest.java) (JUnit 5)
See "How to test a JVM language?" in "Testing FAQ" on how to set up your test environment to obtain the required Mock JDK automatically.
When writing a light test, it is possible to specify the requirements of the project used in test, such as the module type, the configured SDK (SDK), facets (Facet), libraries (Library), etc. It is done by extending the LightProjectDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/LightProjectDescriptor.java) class and returning the project descriptor (usually stored in static final field) from getProjectDescriptor().
Before executing each test, the project instance will be reused if the test case returns the same project descriptor as the previous one or recreated if the descriptor is different (equals() = false).
The standard way of writing a heavy test is to extend HeavyPlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java).
In 2019.3, PlatformTestCase has been renamed to HeavyPlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/HeavyPlatformTestCase.java) reflecting its "heavy test" characteristics.
Examples:
ModuleDeleteProviderTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-tests/testSrc/com/intellij/openapi/roots/ui/configuration/actions/ModuleDeleteProviderTest.java)
FacetTypeUnloadingTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/idea-ui/testSrc/com/intellij/facet/FacetTypeUnloadingTest.kt)
SourceFolderManagerTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/service/project/manage/SourceFolderManagerTest.kt)
If a test requires a multi-module project, using a heavy test is required. The following code snippet presents a multi-module Java project setup:
TestFixtureBuilder<IdeaProjectTestFixture> projectBuilder =
IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName());
// fixture must be created before adding modules:
myFixture = JavaTestFixtureFactory.getFixtureFactory()
.createCodeInsightFixture(projectBuilder.getFixture());
// add and configure modules:
JavaModuleFixtureBuilder<?> builder1 =
projectBuilder.addModule(JavaModuleFixtureBuilder.class);
// optionally, configure the module, e.g.:
// builder1.setLanguageLevel(...);
// builder1.addJdk(...);
JavaModuleFixtureBuilder<?> builder2 =
projectBuilder.addModule(JavaModuleFixtureBuilder.class);
// configure another module...The test fixture creates a test project environment. Unless you customize the project creation, the test project will have one module with one source root called src. The test project files exist either in a temporary directory or in an in-memory file system, depending on which implementation of TempDirTestFixture (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/TempDirTestFixture.java) is used.
BasePlatformTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/BasePlatformTestCase.java) (renamed from LightPlatformCodeInsightFixtureTestCase in 2019.2) uses an in-memory implementation; if you set up the test environment by calling IdeaTestFixtureFactory.createCodeInsightFixture(), you can specify the implementation to use.
If your tests use the in-memory implementation, and you abort the execution of your tests, the persisted filesystem caches may get out of sync with the in-memory structures, and you may get spurious errors in your tests. If you get an unexpected error after a series of successful runs, try rerunning the test, and if that doesn't help, delete the "system" subdirectory in your sandbox directory ("The Development Instance Sandbox Directory" in "IDE Development Instance").
In your plugin, you usually store the test data for your tests (such as files on which plugin features will be executed and expected output files) in the testdata directory. This is just a directory under your plugin's content root, but not under a source root. Files in testdata usually are not valid source code and must not be compiled.
To specify the location of testdata, you must override the getTestDataPath() method. The default implementation assumes running as part of the IntelliJ Platform source tree and is not appropriate for third-party plugins.
A widespread pattern in IntelliJ Platform tests is to use the test method's name being executed as the base for building the testdata file paths. This allows us to reuse most of the code between different test methods that test various aspects of the same feature, and this approach is also recommended for third-party plugin tests. The name of the test method can be retrieved using UsefulTestCase.getTestName().
If your plugin builds on top of Java support, please see "How to test a JVM language?" in "Testing FAQ" to set up your test environment to obtain the required Mock JDK automatically.
To copy files or directories from your testdata directory to the test project directory, you can use the copyFileToProject() and copyDirectoryToProject() methods from CodeInsightTestFixture (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java).
Most operations in plugin tests require a file open in the in-memory editor, in which highlighting, completion, and other operations will be performed. The in-memory editor instance is returned by CodeInsightTestFixture.getEditor(). To copy a file from the testdata directory to the test project directory and immediately open it in the editor, you can use the CodeInsightTestFixture.configureByFile() or configureByFiles() methods. The latter copies multiple files to the test project directory and opens the first of them in the in-memory editor.
Alternatively, you can use one of the other methods, which take parameters annotated with @TestDataFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/TestDataFile.java). These methods copy the specified files from the testdata directory to the test project directory, open the first of the specified files in the in-memory editor, and then perform the requested operation such as highlighting or code completion.
The IDE supports smart navigation between test code and related test data file(s); see this blog post (https://blog.jetbrains.com/platform/2017/10/improvements-in-testing-intellij-platform-plugins/) for more details.
When a file is opened in the in-memory editor, special markup in the file content can specify the caret position or selection.
You can use one of the following markers:
<caret> specifies the position where the caret should be placed.
<selection> and </selection> specify the start and end of the selected text range.
<block> and </block> specify the column selection's start and end points.
In most cases, once you have the necessary files copied to the test project and loaded into the in-memory editor, writing the test itself involves invoking your plugin code and has few dependencies on the test framework.
However, for many common cases, the framework provides helper methods that can make testing easier:
type() simulates the typing of a character or string into the in-memory editor.
performEditorAction() simulates the execution of an action in the in-memory editor context.
complete() simulates code completion invocation and returns the list of lookup elements displayed in the completion list (or null if the completion had no suggestions or one suggestion which was auto-inserted).
findUsages() simulates the invocation of Find Usages and returns the found usages.
findSingleIntention() in combination with launchAction() simulate the invocation of an intention action or inspection quick fix with the specified name.
renameElementAtCaret() or rename() simulate the execution of a rename refactoring.
To compare the results of executing the action with the expected results, you can use the checkResultByFile() method. The file with the expected results can also contain markup ("Special Markup" in "Test Project and Testdata Directories") to specify the expected caret position or selected text range. Suppose you're testing an action that modifies multiple files (a project-wide refactoring, for example). In that case, you can compare an entire directory under the test project with the expected output using PlatformTestUtil.assertDirectoriesEqual().
See Useful Classes ("Useful Classes" in "Testing FAQ") for other common testing functionality.
When writing plugin tests, a common task is testing various kinds of highlighting (inspections, annotators, parser error highlighting, etc.). The IntelliJ Platform provides a dedicated utility and markup format for this task.
To test the highlighting for the file currently loaded into the in-memory editor, invoke CodeInsightTestFixture.checkHighlighting() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java). The parameters to the method specify which severities should be taken into account when comparing the results with the expected results: errors are always taken into account, whereas warnings, weak warnings, and infos are optional. To ignore verifying additional highlighting, set parameter ignoreExtraHighlighting to true.
Alternatively, you can use CodeInsightTestFixture.testHighlighting(), which loads a testdata file (Test Project and Testdata Directories) into the in-memory editor and highlights it as a single operation.
Example: Custom Language Support Tutorial: Testing Annotator (4. Annotator Test)
If you need to test inspections, they must be enabled explicitly. This is done by calling CodeInsightTestFixture.enableInspections() in the setup method of your test or directly in a test method, before the call to CodeInsightTestFixture.checkHighlighting().
To test syntax highlighting provided by Lexer (Implementing Lexer), use EditorTestUtil.testFileSyntaxHighlighting() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java).
This method takes the tested file and the answer file describing expected highlighting information for each token. The answer file format is as follows:
token_value
EXPECTED_TEXT_ATTRIBUTE_KEY => FALLBACK_KEY
string␣value
MY_TEXT_ATTRIBUTE_KEY => MY_FALLBACK_KEY => DEFAULT_STRINGIt starts with token value, which is the actual token value from the tested file (space characters in token value are replaced with ␣). It is followed by indented list of text attribute keys, starting with the key defined for the given token, followed by fallback keys separated by => .
Creating an answer file from scratch would be cumbersome process, so it is recommended to provide only the tested file and executing the test. Test will fail and the expected answer file will be generated. Review the generated file carefully, and provide fixes to the syntax highlighter and the answer file if needed.
The expected results of the highlighting are specified directly in the source file. The platform supports an extensive XML-like markup language for this. In its simplest form, the markup looks like this:
<warning descr="expected warning message">code to be highlighted</warning>A more realistic example, embedded in Java test data to be highlighted:
public int <warning descr="The compareTo() method does not reference 'foo' which is referenced from equals(); inconsistency may result">compareTo</warning>(Simple other) {
return 0;
}The tag name specifies the severity of the expected highlighting. The following severities are supported:
<error>
<warning>
<weak_warning>
<info>
<inject> for an injected fragment (Language Injection)
<symbolName> for a marker that highlights an identifier according to its type
any custom severity can be referenced by its name
The tag can also have the following optional attributes.
Message
descr expected message associated with the highlighter (if not specified, any text will match). If the message contains a quotation mark, it can be escaped by putting two backslash characters before it.
tooltip expected tooltip message
Visual
textAttributesKey expected TextAttributesKey (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java) referenced by its externalName
foregroundColor, backgroundColor, effectColor expected colors for the highlighting
effectType expected effect type for the highlighting (see EffectType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/editor/markup/EffectType.java))
fontType expected font style for the highlighting (0 - normal, 1 - bold, 2 - italic, 3 - bold italic)
Nested tags are supported:
<warning>warning_highlight<info>warning_and_info_highlight</info>warning_highlight</warning>Overlapping tags (annotations) are currently not supported in the test framework (but displayed correctly in the editor, albeit this is not an officially supported scenario):
<warning>warning_highlight<info>warning-and_info_highlight</warning>info_highlight</info>This page lists a number of common questions/issues and techniques useful for testing plugins.
Check out this step-by-step tutorial (Testing a Custom Language Plugin) teaching how to write and run automated tests for your custom language plugin. Also, code samples comparing_string_references_inspection (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/comparing_string_references_inspection) and conditional_operator_intention (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/conditional_operator_intention) demonstrate using tests.
UsefulTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java)
PlatformTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java)
CodeInsightTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestUtil.java)
EditorTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/EditorTestUtil.java)
See "UI Tests" in "Testing Overview" for UI integration tests.
ProjectViewTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/ProjectViewTestUtil.java)
TestLookupElementPresentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/TestLookupElementPresentation.java)
IconTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/ui/IconTestUtil.java)
EdtTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/common/src/EdtTestUtil.java)
Please see notes ("2021.3" in "Incompatible Changes in IntelliJ Platform and Plugins API 2021.*").
Always call super.tearDown() inside finally {..} block of your test class to avoid leaks and side effects from previously run (failed) tests:
protected void tearDown() throws Exception {
try {
// test specific tear down calls
} catch (Exception e) {
addSuppressedException(e);
} finally {
super.tearDown();
}
}override fun tearDown() {
try {
// test specific tear down calls
} catch (e: Throwable) {
addSuppressedException(e)
} finally {
super.tearDown()
}
}Avoid OS-specific assumptions (e.g., filesystem case-sensitivity, hardcoded separator instead of java.io.File.separator, default encoding, line endings).
Use ordered collections or UsefulTestCase.assertUnorderedCollection() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java).
Code deferring execution (e.g., via Application.invokeLater()) might not run during test execution (and possibly fails in production, too). Use Application.invokeLater(runnable, myProject.getDisposed()).
In some situations, added or changed files (e.g. XML DTDs provided by a plugin) are not refreshed in Virtual File System. In such cases, simply delete test-system/caches in your sandbox directory ("The Development Instance Sandbox Directory" in "IDE Development Instance") and try again.
Provide JVM system properties idea.log.debug.categories or idea.log.trace.categories to specify logger category name, respectively. Multiple categories can be set using a comma separated value list.
Sample Set DEBUG level for categories com.my.plugin.ui and com.my.plugin.backend:
tasks {
test {
systemProperty("idea.log.debug.categories", "com.my.plugin.ui,com.my.plugin.backend")
}
}test {
systemProperty("idea.log.debug.categories", "com.my.plugin.ui,com.my.plugin.backend")
}Set system property idea.split.test.logs to true to generate separate test log files in splitTestLogs subdirectory for failing tests (WARN/ERROR level messages) (2021.3).
Annotate with @TestOnly (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/TestOnly.java), usages will be highlighted via inspection JVM languages | Test-only usage in production code.
To mark members whose visibility is higher than necessary to be used from tests, use @VisibleForTesting (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/VisibleForTesting.java)
Use FileBasedTestCaseHelper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/FileBasedTestCaseHelper.java), please see its Javadoc for instructions.
Use UsefulTestCase.getTestName() or create your own annotation(s) which can be checked via UsefulTestCase.annotatedWith().
Use PlatformTestUtil.startPerformanceTest() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java) to assert machine-adjusted metrics.
Use PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java).
Use DefaultLogger.disableStderrDumping() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/diagnostic/DefaultLogger.java) passing getTestRootDisposable().
Use ExternalResourceManagerExImpl.registerResourceTemporarily() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-impl/src/com/intellij/javaee/ExternalResourceManagerExImpl.java) passing getTestRootDisposable().
Provide dedicated test implementation via testServiceImplementation in service declaration ("Declaring a Service" in "Services"), or use ServiceContainerUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/ServiceContainerUtil.kt).
Use ExtensionTestUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/ExtensionTestUtil.kt).
If possible, use approach. Otherwise, call com.intellij.util.TimeoutUtil.sleep().
Use WaitFor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/util/WaitFor.java).
Plugins supporting a JVM language may require JDK and language standard library to be set up in a test project, so that classes like java.lang.String can be correctly resolved during tests. Tests extending LightJavaCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase.java) use one of the mock JDKs distributed with the IntelliJ Community project (https://github.com/JetBrains/intellij-community) sources (notice java/mockJDK-$JAVA_VERSION$ directories). These JAR files are not available in plugin project dependencies, so the IntelliJ Community sources must be checked out to the machine running the tests, and sources' location must be provided to the test framework. It's done by setting the idea.home.path system property to the absolute path of the checked-out sources in the test task configuration:
test {
systemProperty("idea.home.path", "/path/to/intellij-community-sources")
}test {
systemProperty "idea.home.path", "/path/to/intellij-community-sources"
}The default JDK version used by the test framework depends on the target platform version and is the latest supported version. The easiest way to change the JDK version to a custom one is by overriding LightJavaCodeInsightFixtureTestCase.getProjectDescriptor() and using one of the predefined project descriptors in LightJavaCodeInsightFixtureTestCase. If a project descriptor requires more customizations, its getSdk() method can use one of the IdeaTestUtil.getMockJdk*() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/IdeaTestUtil.java) methods.
Sometimes, testing a JVM language requires adding standard or other libraries to a test project. If a required library is available in the Maven repository, use MavenDependencyUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/MavenDependencyUtil.java), e.g.:
MavenDependencyUtil.addFromMaven(model,
"org.jetbrains.kotlin:kotlin-stdlib:1.6.10");For light tests (Light and Heavy Tests), use convenience method DefaultLightProjectDescriptor.withRepositoryLibrary() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/DefaultLightProjectDescriptor.java) and JavaModuleFixtureBuilder.addMavenLibrary() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/builders/JavaModuleFixtureBuilder.java) for heavy tests (Light and Heavy Tests).
If a required library is an unpublished JAR file, use PsiTestUtil.addLibrary() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/PsiTestUtil.java) or addProjectLibrary() method and the JAR file path, e.g.:
PsiTestUtil.addLibrary(model,
"internal-library", getTestDataPath(), "internal-library-2.0.jar");Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
IntelliJ Platform is a powerful platform for building development tools targeting any language. Most of the IDE features consist of language-independent (provided by the platform) and language-specific parts. Supporting a particular feature for a new language can be achieved with a small amount of effort: a plugin must implement only the language-specific part.
This part of the documentation explains the main concepts of the Language API and guides you through the sequence of steps that are usually required to develop a custom language plugin. You can obtain additional information about the Language API from the Javadoc comments for the Language API classes and from the Properties language support source code, which is part of the IntelliJ IDEA Community Edition (https://github.com/JetBrains/intellij-community) source code.
If you prefer a full example to the detailed descriptions offered in this section, please check out a step-by-step tutorial on how to create custom language support for Simple Language: Custom Language Support Tutorial. Corresponding steps from the tutorial are linked under the " Example " section on each page of this reference.
The webinar How We Built Comma, the Raku IDE, on the IntelliJ Platform (https://blog.jetbrains.com/platform/2020/01/webinar-recording-how-we-built-comma-the-raku-ide-on-the-intellij-platform/) offers an excellent introduction as well.
Code Hierarchy
See Language Server Protocol (LSP) for supporting language servers.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Product Help: File type associations (https://www.jetbrains.com/help/idea/creating-and-registering-file-types.html)
The first step in developing a custom language plugin is registering a file type associated with the language.
The IDE typically determines the type of a file by looking at its filename or extension.
A custom language file type is a class derived from LanguageFileType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java), which passes a Language (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Language.java) subclass to its base class constructor.
When targeting 2019.2 or later only, use com.intellij.fileType extension point to register LanguageFileType implementation and instance via implementationClass and fieldName attributes. Also, name and language must be declared matching FileType.getName() and ID of language returned from LanguageFileType.getLanguage(), respectively.
To associate the file type in the IDE, specify one or more associations as listed in the following table.
Association type | Attribute | Attribute value |
|---|---|---|
Filename extension(s) | extensions | Semicolon-separated list of extensions, without . prefix |
Hard coded file name(s) | fileNames/fileNamesCaseInsensitive | Semicolon-separated list of exact (case-insensitive) file names |
Filename pattern(s) | patterns | Semicolon-separated list of patterns (* and ?) |
Hashbang (2020.2+) | hashBangs | Semicolon-separated list of hash bang patterns |
The FileTypeFactory approach is deprecated. Use it only when the plugin supports platform versions older than 2019.2.
To register a file type, the plugin developer provides a subclass of FileTypeFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/openapi/fileTypes/FileTypeFactory.java), which is registered via the com.intellij.fileTypeFactory extension point.
Examples
Custom Language Support Tutorial: Language and File Type (2. Language and File Type)
LanguageFileType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java) subclass in Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesFileType.java)
To verify that the file type is registered correctly, you can implement the LanguageFileType.getIcon() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java) method and verify that the correct icon (see Working with Icons) is displayed for files associated with your file type.
If you want IDEs to show a hint prompting users that your plugin supports a specific file type, see Plugin Recommendations (https://plugins.jetbrains.com/docs/marketplace/intellij-plugin-recommendations.html).
To control file type association with the IDE in the operating system, implement OSFileIdeAssociation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/OSFileIdeAssociation.java) (2020.3).
The lexer, or lexical analyzer (https://en.wikipedia.org/wiki/Lexical_analysis), defines how a file's contents are broken into tokens. The lexer serves as a foundation for nearly all features of custom language plugins, from basic syntax highlighting to advanced code analysis features.
The API for the lexer is defined by the Lexer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lexer/Lexer.java) interface.
The IDE invokes the lexer in three main contexts, and the plugin can provide different lexer implementations if needed:
Syntax highlighting ("Lexer" in "Syntax and Error Highlighting"): The lexer is returned from the implementation of the SyntaxHighlighterFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighterFactory.java) interface which is registered in the com.intellij.lang.syntaxHighlighterFactory extension point.
Building the syntax tree of a file (3. Grammar and Parser): the lexer is expected to be returned from ParserDefinition.createLexer() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java) implementation registered in the com.intellij.lang.parserDefinition extension point.
Building the index of the words contained in the file: if the lexer-based words scanner implementation is used, the lexer is passed to the DefaultWordsScanner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java) constructor. See also Find Usages.
The lexer used for syntax highlighting can be invoked incrementally to process only the file's changed part. In contrast, lexers used in other contexts are always called to process an entire file or a complete language construction embedded in a different language file.
A lexer that can be used incrementally may need to return its state, which means the context corresponding to each position in a file. For example, a Java lexer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java) could have separate states for top-level context, comment context, and string literal context.
An essential requirement for a syntax highlighting lexer is that its state must be represented by a single integer number returned from Lexer.getState(). That state will be passed to the Lexer.start() method, along with the start offset of the fragment to process, when lexing is resumed from the middle of a file. Lexers used in other contexts can always return 0 from getState().
The easiest way to create a lexer for a custom language plugin is to use JFlex (https://jflex.de).
Classes FlexLexer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lexer/FlexLexer.java) and FlexAdapter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lexer/FlexAdapter.java) adapt JFlex lexers to the IntelliJ Platform Lexer API. A patched version of JFlex (https://github.com/JetBrains/intellij-deps-jflex) can be used with the lexer skeleton file idea-flex.skeleton (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/tools/lexer/idea-flex.skeleton) located in the IntelliJ IDEA Community Edition (https://github.com/JetBrains/intellij-community) source to create lexers compatible with FlexAdapter. The patched version of JFlex provides a new command-line option --charat that changes the JFlex generated code to work with the IntelliJ Platform skeleton. Enabling --charat option passes the source data for lexing as a java.lang.CharSequence and not as an array of characters.
For developing lexers using JFlex, the Grammar-Kit plugin (https://plugins.jetbrains.com/plugin/6606-grammar-kit) can be useful. It provides syntax highlighting and other useful features for editing JFlex files (*.flex).
Lexers, and in particular JFlex-based lexers, need to be created so that they always match the entire contents of the file, without any gaps between tokens, and generate special tokens for characters which are not valid at their location. Lexers must never abort prematurely because of an invalid character.
Examples:
JFlex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/parsing/Properties.flex) definition file for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Lexer (4. Lexer and Parser Definition)
Types of tokens for lexers are defined by instances of IElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IElementType.java). Many token types common for all languages are defined in the TokenType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/TokenType.java) interface. Custom language plugins should reuse these token types wherever applicable. For all other token types, the plugin needs to create new IElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IElementType.java) instances and associate with the language in which the token type is used. The same IElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IElementType.java) instance should be returned every time a particular token type is encountered by the lexer.
Example: Token types (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/parsing/PropertiesTokenTypes.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Groups of related types (e.g., keywords) can be defined using TokenSet (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/TokenSet.java). All TokenSet for a language should be grouped in a dedicated $Language$TokenSets class for re-use.
Example: GroovyTokenSets (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/lang/psi/GroovyTokenSets.java)
An important feature that can be implemented at the lexer level is mixing languages within a file, such as embedding fragments of Java code in some template language. Suppose a language supports embedding its fragments in another language. In that case, it needs to define the chameleon token types for different types of fragments that can be embedded, and these token types need to implement the ILazyParseableElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/ILazyParseableElementType.java) interface. The enclosing language's lexer needs to return the entire fragment of the embedded language as a single chameleon token, of the type defined by the embedded language. To parse the contents of the chameleon token, the IDE will call the parser of the embedded language through a call to ILazyParseableElementType.parseContents() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/ILazyParseableElementType.java).
Parsing files in IntelliJ Platform is a two-step process.
First, an abstract syntax tree (AST) is built, defining the structure of the program. AST nodes are created internally by the IDE and are represented by instances of the ASTNode (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ASTNode.java) class. Each AST node has an associated element type IElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IElementType.java) instance, and the element types are defined by the language plugin. The AST tree's top-level node for a file needs to have a special element type, which extends the IFileElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IFileElementType.java) class.
The AST nodes have a direct mapping to text ranges in the underlying document. The bottom-most nodes of the AST match individual tokens returned by the lexer (Implementing Lexer), and higher-level nodes match multiple-token fragments. Operations performed on nodes of the AST tree, such as inserting, removing, reordering nodes, and so on, are immediately reflected as changes to the underlying document's text.
Second, a Program Structure Interface (PSI) tree is built on top of the AST, adding semantics and methods for manipulating specific language constructs. Nodes of the PSI tree are represented by classes implementing the PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) interface and are created by the language plugin in the ParserDefinition.createElement() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java) method. The top-level node of the PSI tree for a file needs to implement the PsiFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFile.java) interface and is created in the ParserDefinition.createFile() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java) method.
Example: ParserDefinition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/parsing/PropertiesParserDefinition.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Using TokenSets in ParserDefinitionTo avoid unnecessary classloading when initializing the ParserDefinition extension point implementation, all TokenSet return values should use constants from a dedicated $Language$TokenSets class.
See also inspection Plugin DevKit | Code | Non-platform TokenSet declared in ParserDefinition (2023.3).
The PSI's lifecycle is described in more detail in Fundamentals (Fundamentals).
The base classes for the PSI implementation, including PsiFileBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/extapi/psi/PsiFileBase.java), the base implementation of PsiFile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiFile.java), and ASTWrapperPsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java), the base implementation of PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java), are provided by IntelliJ Platform.
While coding parser manually is quite possible, we highly recommend generating parser and corresponding PSI classes from BNF grammars using Grammar-Kit (https://plugins.jetbrains.com/plugin/6606-grammar-kit) plugin. Besides code generation, it provides various features for editing grammar files: syntax highlighting, quick navigation, refactorings, etc. as well as integration with Gradle via Gradle Grammar-Kit Plugin. The Grammar-Kit plugin is built using its own engine; its source code and documentation can be found on GitHub (https://github.com/JetBrains/Grammar-Kit).
For re-using existing ANTLRv4 grammars, see antlr4-intellij-adaptor (https://github.com/antlr/antlr4-intellij-adaptor) third-party library.
The language plugin provides the parser implementation as an implementation of the PsiParser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/PsiParser.java) interface, returned from ParserDefinition.createParser() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java). The parser receives an instance of the PsiBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/PsiBuilder.java) class, which is used to get the stream of tokens from the lexer (Implementing Lexer) and to hold the intermediate state of the AST being built.
The parser must process all tokens returned by the lexer up to the end of the stream (until PsiBuilder.getTokenType() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/PsiBuilder.java) returns null) — even if the tokens are not valid according to the language syntax.
The parser works by setting pairs of markers (PsiBuilder.Marker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/PsiBuilder.java) instances) within the stream of tokens received from the lexer. Each pair of markers defines the range of lexer tokens for a single node in the AST tree. If a pair of markers is nested in another pair (starts after its start and ends before its end), it becomes the outer pair's child node.
The element type for the marker pair and for the AST node created from it is specified when the end marker is set, which is done by making the call to PsiBuilder.Marker.done(). Also, it is possible to drop a start marker before its end marker has been set. The drop() method drops only a single start marker without affecting any markers added after it, and the rollbackTo() method drops the start marker and all markers added after it and reverts the lexer position to the start marker. These methods can be used to implement lookahead when parsing.
The method PsiBuilder.Marker.precede() is useful for right-to-left parsing when you don't know how many markers you need at a specific position until you read more input. For example, a binary expression a+b+c needs to be parsed as ( (a+b) + c ). Thus, two start markers are needed at the position of the token 'a', but that is not known until the token 'c' is read. When the parser reaches the '+' token following 'b', it can call precede() to duplicate the start marker at 'a' position, and then put its matching end marker after 'c'.
Examples:
Custom Language Support Tutorial: Grammar and Parser (3. Grammar and Parser) (using Grammar-Kit)
Simple PropertiesParser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/parsing/PropertiesParser.java) implementation for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties).
Complex RegExpParser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/RegExpSupport/src/org/intellij/lang/regexp/RegExpParser.java) for RegEx language
An essential feature of PsiBuilder is its handling of whitespace and comments. The types of tokens which are treated as whitespace or comments are defined by getWhitespaceTokens() and getCommentTokens() in ParserDefinition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java). PsiBuilder automatically omits whitespace and comment tokens from the stream of tokens it passes to PsiParser and adjusts the token ranges of AST nodes so that leading and trailing whitespace tokens are not included in the node.
Most languages will not need to override getWhitespaceTokens() which returns the language-agnostic TokenSet.WHITE_SPACE (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/TokenSet.java) by default. The token set returned from ParserDefinition.getCommentTokens() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java) is also used to search for TODO items (https://www.jetbrains.com/help/idea/using-todo.html).
To better understand the process of building a PSI tree for a simple expression, you can refer to the following diagram:

In general, there is no single right way to implement a PSI for a custom language, and the plugin author can choose the PSI structure and set of methods that are the most convenient for the code which uses the PSI (error analysis, refactorings, and so on).
However, one base interface needs to be used by a custom language PSI implementation to support features like Rename Refactoring and Find Usages. Every element which can be renamed or referenced (a class definition, a method definition, and so on) needs to implement the PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) interface, with methods getName() and setName().
Several functions which can be used for implementing and using the PSI can be found in the com.intellij.psi.util package, and in particular in the PsiUtilCore (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/PsiUtilCore.java) and PsiTreeUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/PsiTreeUtil.java) classes.
Use builtin tools and PsiViewer plugin ("3.1 Use Internal Mode and PsiViewer" in "Explore the IntelliJ Platform API") to explore and inspect PSI.
Please see Indexing and PSI Stubs (Indexing and PSI Stubs) for advanced topics.
Product Help: Colors and fonts (https://www.jetbrains.com/help/idea/configuring-colors-and-fonts.html)
Platform UI Guidelines: Inspections (https://jetbrains.design/intellij/text/inspections/)
The syntax and error highlighting are performed on multiple levels: , , and /.
How a particular range of text should be highlighted is defined via TextAttributesKey (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/editor/colors/TextAttributesKey.java). An instance of this class is created for every distinct type of item that should be highlighted (keyword, number, string literal, etc.).
The TextAttributesKey defines the default attributes applied to items of the corresponding type (for example, keywords are bold, numbers are blue, string literals are bold and green). Highlighting from multiple TextAttributesKey items can be layered — for example, one key may define an item's boldness and another one its color.
Looking up existing TextAttributeKeyTo inspect applied TextAttributesKey (s) in the editor for the element at the caret, use Jump to Colors and Fonts action.
The underlying TextAttributeKey's external name for items in Settings | Editor | Color Scheme can be inspected using UI Inspector ("Inspecting Settings" in "Internal Actions - UI Inspector").
The mapping of the TextAttributesKey to specific attributes used in an editor is defined by the EditorColorsScheme (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java) class. It can be configured by the user via Settings | Editor | Color Scheme by providing an implementation of ColorSettingPage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/options/colors/ColorSettingsPage.java) registered in com.intellij.colorSettingsPage extension point. To lookup external name for a setting in the IDE, use UI Inspector ("Inspecting Settings" in "Internal Actions - UI Inspector").
The File | Export | Files or Selection to HTML feature uses the same syntax highlighting mechanism as the editor. Thus, it will work automatically for custom languages that provide a syntax highlighter.
Examples:
ColorSettingsPage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/PropertiesColorsPage.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Color Settings Page (5. Syntax Highlighter and Color Settings Page)
See note about Language Defaults and support for additional color schemes in Color Scheme Management.
The first syntax highlighting level is based on the lexer output and is provided through the SyntaxHighlighter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighter.java) interface. The syntax highlighter returns the TextAttributesKey instances for each token type, which needs special highlighting. For highlighting lexer errors HighlighterColors.BAD_CHARACTER (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/HighlighterColors.java) should be used.
Examples:
SyntaxHighlighter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-api/src/com/intellij/lang/properties/PropertiesHighlighter.java) implementation for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Syntax Highlighter (5. Syntax Highlighter and Color Settings Page)
Creating highlighted code sampleUse HtmlSyntaxInfoUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/HtmlSyntaxInfoUtil.java) to create Lexer-based highlighted code samples, e.g. for usage in documentation.
Semantic highlighting (https://www.jetbrains.com/help/idea/configuring-colors-and-fonts.html#semantic-highlighting) provides an additional coloring layer to improve the visual distinction of several related items (e.g., method parameters, local variables).
Register RainbowVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/codeInsight/daemon/RainbowVisitor.java) in com.intellij.highlightVisitor extension point. must implement RainbowColorSettingsPage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/options/colors/RainbowColorSettingsPage.java) in addition.
The second level of error highlighting happens during parsing. If a particular sequence of tokens is invalid according to the grammar of the language, the PsiBuilder.error() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/PsiBuilder.java) method can highlight the invalid tokens and display an error message showing why they are not valid.
See Syntax Errors on how to programmatically suppress these errors in certain contexts.
The third level of highlighting is performed through the Annotator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/Annotator.java) interface. A plugin can register one or more annotators in the com.intellij.annotator extension point, and these annotators are called during the background highlighting pass to process the elements in the custom language's PSI tree. Attribute language should be set to the Language ID where this annotator applies to.
Annotators can analyze not only the syntax, but also the semantics using PSI, and thus can provide much more complex syntax and error highlighting logic. The annotator can also provide quick fixes to problems it detects. When the file is changed, the annotator is called incrementally to process only changed elements in the PSI tree.
Annotators not requiring information from indexes (Indexing and PSI Stubs) can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs") to work during indexing (e.g., for additional syntax highlighting). (2023.1+)
See also Code Inspections (Code Inspections and Intentions) which offer a more fine-grained control and some additional features.
See Inspections (https://jetbrains.design/intellij/text/inspections/) topic in IntelliJ Platform UI Guidelines on how to write message texts for highlighting/quick fixes.
To highlight a region of text as a warning or error:
holder.newAnnotation(HighlightSeverity.WARNING, "Invalid code") // or HighlightSeverity.ERROR
.withFix(new MyFix(psiElement))
.create();Call createWarningAnnotation()/createErrorAnnotation() on the AnnotationHolder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/AnnotationHolder.java), and optionally calls registerFix() on the returned Annotation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/Annotation.java) object to add a quick fix for the error or warning.
To apply additional syntax highlighting:
holder.newSilentAnnotation(HighlightSeverity.INFORMATION)
.range(rangeToHighlight)
.textAttributes(MyHighlighter.EXTRA_HIGHLIGHT_ATTRIBUTE)
.create();Call AnnotationHolder.createInfoAnnotation() with an empty message and then Annotation.setTextAttributes() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/Annotation.java).
Examples:
Annotator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesAnnotator.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Annotator (7. Annotator)
If the custom language employs external tools for validating files in the language (for example, uses the Xerces library for XML schema validation), it can provide an implementation of the ExternalAnnotator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/ExternalAnnotator.java) interface and register it in com.intellij.externalAnnotator extension point (language attribute must be specified).
The ExternalAnnotator highlighting has the lowest priority and is invoked only after all other background processing has completed. It uses the same AnnotationHolder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/AnnotationHolder.java) interface for converting the output of the external tool into editor highlighting.
To skip running specific ExternalAnnotator for given file, register ExternalAnnotatorsFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/ExternalAnnotatorsFilter.java) extension in com.intellij.daemon.externalAnnotatorsFilter extension point.
To enable running ExternalAnnotator during indexing in dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"), it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs") (2023.3).
Existing highlighting can be suppressed programmatically in certain contexts, see Controlling Highlighting.
To force re-highlighting all open or specific file(s) (e.g., after changing plugin specific settings), use DaemonCodeAnalyzer.restart() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/daemon/DaemonCodeAnalyzer.java).
Since 2024.1, inspections (Code Inspections and Intentions) and annotators do not run sequentially on each PsiElement anymore. Instead, they're run in parallel on all relevant PSI independently with the following consequences.
Independent Annotators
Annotators are run independent of each other: if an annotator found an error, it no longer stops annotating the PsiElement's parents. Effectively, there is "more" highlighting now.
Highlight Range
Producing highlights must be done as close as possible for the relevant PsiElement. For example, instead of
annotate(PsiFile) {
<<highlight all relevant identifiers>>
}this approach should be used:
annotate(PsiIdentifier) {
<<highlight this identifier if it's relevant>>
}The latter version:
performs faster highlighting — it doesn’t have to wait until all other identifiers are visited
removes outdated highlights faster — right after the identifier was visited and the annotator didn't produce a highlighting anymore
One of the most important and tricky parts in implementing a custom language PSI is resolving references. Resolving references gives users the ability to navigate from a PSI element usage (accessing a variable, calling a method, etc.) to the declaration of that element (the variable's definition, a method declaration, and so on).
This feature is needed in order to support the Navigate | Declaration or Usages action invoked by Ctrl/CmdB or clicking the mouse button while holding Ctrl/Cmd key, and it is a prerequisite for implementing the Find Usages (Find Usages) action, the Rename Refactoring (Rename Refactoring) and Code Completion (Code Completion).
The View | Quick Definition action is based on the same mechanism, so it becomes automatically available for all references that can be resolved by the language plugin. To customize the exact document range to show in the popup (e.g., include "surrounding" code or comments), provide ImplementationTextSelectioner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hint/ImplementationTextSelectioner.java) registered in com.intellij.lang.implementationTextSelectioner extension point.
All PSI elements which work as references (for which the Navigate | Declaration or Usages action applies) need to implement the PsiElement.getReference() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) method and to return a PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) implementation from that method. The PsiReference can be implemented by the same class as PsiElement, or by a different class. An element can also contain multiple references (for example, a string literal can contain multiple substrings which are valid fully-qualified class names), in which case it can implement PsiElement.getReferences() and return the references as an array. To optimize PsiElement.getReferences() performance, consider implementing HintedReferenceHost (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/HintedReferenceHost.java) to provide additional hints.
The primary method of the PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) interface is resolve(), which returns the element to which the reference points, or null if it was not possible to resolve the reference to a valid element (for example, should it point to an undefined class). The resolved element should implement the PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) interface. In order to enable more advanced functionality, prefer implementing PsiNameIdentifierOwner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNameIdentifierOwner.java) over PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) where possible.
While the referencing element and the referenced element both may have a name, only the element which introduces the name (e.g., the definition int x = 42) needs to implement PsiNamedElement. The referencing element at the point of usage (e.g., the x in the expression x + 1) should not implement PsiNamedElement since it does not have a name.
A counterpart to the resolve() method is isReferenceTo(), which checks if the reference resolves to the specified element. The latter method can be implemented by calling resolve() and comparing the result with the passed PSI element. Still, additional optimizations are possible (for example, performing the tree walk only if the element text is equal to the text of the reference).
Examples:
Reference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/ResourceBundleReference.java) to a ResourceBundle in the Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Reference Contributor (10. Reference Contributor)
There is a set of interfaces that can be used as a base for implementing resolve support, namely the PsiScopeProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/scope/PsiScopeProcessor.java) interface and the PsiElement.processDeclarations() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) method. These interfaces have several extra complexities that are unnecessary for most custom languages (like support for substituting Java generics types). Still, they are required if the custom language can have references to Java code. If Java interoperability is not required, the plugin can forgo the standard interfaces and provide its own, different implementation of resolve.
Please see also "Cache Results of Heavy Computations" in "PSI Performance".
The implementation of resolve based on the standard helper classes contains the following components:
A class implements the PsiScopeProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/scope/PsiScopeProcessor.java) interface, which gathers the possible declarations for the reference and stops the resolve process when it has successfully completed. The primary method which needs to be implemented is execute(), which is called to process every declaration encountered during the resolve, and returns true if the resolve needs to be continued or false if the declaration has been found. The methods getHint() and handleEvent() are used for internal optimizations and can be left empty in the PsiScopeProcessor implementations for custom languages.
A function which walks the PSI tree up from the reference location until the resolve has successfully completed or until the end of the resolve scope has been reached. If the target of the reference is located in a different file, the file can be located, for example, using FilenameIndex.getFilesByName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/FilenameIndex.java) (if the file name is known) or by iterating through all custom language files in the project (iterateContent() in the ProjectFileIndex (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectFileIndex.java) interface obtained from ProjectRootManager.getFileIndex() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/ProjectRootManager.java)).
The individual PSI elements, on which the processDeclarations() method is called during the PSI tree walk. If a PSI element is a declaration, it passes itself to the execute() method of the PsiScopeProcessor passed to it. Also, if necessary, according to the language scoping rules, a PSI element can pass the PsiScopeProcessor to its child elements.
An extension of the PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) interface, which allows a reference to resolve to multiple targets, is the PsiPolyVariantReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiPolyVariantReference.java) interface. The targets to which the reference resolves are returned from the multiResolve() method. The Navigate | Declaration or Usages action for such references allows the user to choose a navigation target in a popup. The implementation of multiResolve() can be also based on PsiScopeProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/scope/PsiScopeProcessor.java), and can collect all valid targets for the reference instead of stopping when the first valid target is found.
Implement HighlightedReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightedReference.java) to add additional highlighting for non-obvious places (e.g., inside String literals). Such references will automatically be highlighted using String | Highlighted reference text attributes from Settings | Editor | Color Scheme | Language Defaults (2022.2).
This API is available starting from 2020.3 and is currently in development and thus in an experimental state.
A symbol is a semantic element in some model, e.g., language or framework model.
The IntelliJ Platform uses Symbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/Symbol.java) to represent symbols, and Symbol serves as a link between Platform APIs, such as navigation, finding usages, or renaming. This API allows implementing the same functionalities as in the References and Resolve (References and Resolve) mechanism, but it is a more abstract concept not limited to connecting only PSI elements. The platform obtains the target symbol from a declaration ("Declarations" in "Declarations and References") or by resolving a reference ("References" in "Declarations and References") and then uses it to perform an action. The PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) is considered as an element in the source tree (enhanced ASTNode). Symbol decouples semantic actions from PSI (Program Structure Interface (PSI)).
A Symbol is not required to be backed by a PsiElement, and it is incorrect to try to obtain the PsiElement from a Symbol. Symbol is not required to be bound to a Project as well, meaning the same instance might be shared between projects (Project).
Examples:
Java local variable is a symbol in Java language model, it's backed by a PsiVariable element.
Compiled class is a symbol in JVM model, it's backed by JDK library stubs, and it's not bound to any project.
Spring Bean is a symbol in Spring framework model (Spring API), it's defined on-the-fly by framework support (not backed by a PsiElement) and bound to a Project.
Database column is a symbol defined by data source (not backed by a PsiElement) and not bound to a Project since database elements might be shared between projects.
The Symbol instance is expected to stay valid within a single read action, which means it's safe to pass the instance to different APIs. A Symbol instance should not be referenced between read actions. One should create a pointer via Symbol.createPointer() in the current read action, and then call Pointer.dereference() to obtain a Symbol instance in the subsequent read action.
This API is available starting from 2020.3 and is currently in development and thus in an experimental state.
Each symbol (Symbols) may be declared in zero or more places, for example:
a C# partial class is a symbol with several declarations;
a property key is a symbol possibly declared in several files simultaneously;
a Java local variable is a symbol with a single declaration;
and a file is a symbol without declarations; it has only references.
Declarations in PSI elements are implementations of PsiSymbolDeclaration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiSymbolDeclaration.java).
To report a declaration in a PSI element, either:
implement and register PsiSymbolDeclarationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiSymbolDeclarationProvider.java) in com.intellij.psi.declarationProvider EP;
or implement PsiSymbolDeclaration directly in the PsiElement.
References from PSI elements are implementations of PsiSymbolReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiSymbolReference.java) interface.
The main method of PsiSymbolReference is resolveReference(), which returns the collection of symbols to which the reference points, plus additional data. If it is not possible to resolve the reference, for example, if it points to an undefined class, an empty collection gets returned. A counterpart to the resolveReference() method is PsiSymbolReference.resolvesTo(), which checks if the reference resolves to the specified symbol. This method can be implemented to walk the tree only if the element's text is equal to the reference's text.
For convenience, if the reference can possibly be resolved to a single symbol without additional data, then it might be extended from SingleTargetReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/SingleTargetReference.java).
Own references are the references found in PSI elements, which are considered as references by the language.
Example: PSI element representing x in x * 2 Java expression has an Own reference to a local Java variable, e.g., var x = 42, because this is a reference from Java language point of view, and Java language support uses it, e.g., for code analysis.
To provide Own references by the PsiElement, implement PsiElement.getOwnReferences() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) in the PsiElement. If the element contains a single reference, Collections.singletonList() can be used.
External references are the references which are not considered as references by the host language. The language support should not rely on their existence/absence, because they might be contributed by other plugins.
Example: PSI element representing "users.txt" in new File("users.txt") Java expression is a string literal from Java language point of view, but there is a plugin which knows that this literal references a file name, and provides such reference.
External references might be contributed to PSI elements that implement PsiExternalReferenceHost (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiExternalReferenceHost.java). To allow other plugins to contribute references of PsiElement, implement PsiExternalReferenceHost in the PsiElement. To contribute an External reference to the existing PsiExternalReferenceHost, implement and register PsiSymbolReferenceProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java).
Implicit references are the references which should be part of the mechanism to obtain a target by a reference, without the inverse ability to search or rename such references by a target.
Example: var keyword in var x = new Person() Java declaration has an Implicit reference, because it doesn't make sense to obtain the reference by the target class.
At the same time, it's possible:
to navigate to the class by Ctrl-Click on var;
to start a refactoring (e.g., rename) from the class targeted by this reference;
to view documentation of the class targeted by this reference.
To provide an Implicit reference, implement and register ImplicitReferenceProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/ImplicitReferenceProvider.java) in com.intellij.psi.implicitReferenceProvider extension point.
This API is available starting from 2022.3.1 and is currently in development and thus in an experimental state.
Web Symbols is a framework built on top of the platform's Symbol API (Symbols). Its primary use is to reduce boilerplate when building support for web technologies, however one can take advantage of it for any kind of technology.
Web Symbols API reduces work needed to provide reference resolution, find usages, documentation and rename refactoring. It also provides an efficient JSON format (Web Types) for static symbol definitions. Web Symbols core advantage, however, is the ability to evaluate symbol patterns.
Web frameworks tend to have custom syntaxes to enable various things, e.g. through HTML attribute name (see Model Queries example ("Example" in "Implementing Web Symbols") to see Vue directive syntax). The pattern evaluator is able to recognize symbols in different sections of the element name and provide reference resolution, documentation, usage occurrence, etc. It also supports rename refactoring for symbols originating from source code. Another advantage is the flexibility of Web Symbols query. It is very easy to contribute new symbols from various sources, which works perfectly for multi-sourced (e.g. source code, library code, Web Types) applications.
Web Symbols API is not designed to create a support for new languages, but to rather work on meta level, to support frameworks or libraries, which are giving additional meaning to the existing language features. Currently, IDEs provide built-in integration for following language features (see Web Symbols Integration with Language Features):
HTML: elements, attributes and attribute values
CSS: properties, custom properties, functions, classes, pseudo-elements, pseudo-classes and parts
JavaScript: string-literals, object properties, object literals and symbols (in JavaScript)
There's also the option to write integration for other languages.
The following sub-pages provide information on how to implement Web Symbols for plugins and how to define them statically through JSON schemas:
The core element of the framework is a WebSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/WebSymbol.kt), representing an entity in the Web Symbols model. This symbol is characterized by namespace, kind and name properties, with its lifecycle encapsulated within a single read action. To ensure its survival between read actions, use WebSymbol.createPointer() to create a symbol pointer. Provided the symbol remains valid, dereferencing the pointer will return a new instance of the symbol. It should be noted that during a write action, the symbol might not survive a PSI tree commit. Therefore, creating a pointer prior to the commit and dereferencing it post-commit is advised.
The property namespace describes which language or concept (not tied to a particular language) the symbol belongs to, and kind describes which group of symbols within that particular language or concept it belongs to. Examples:
a CSS property: namespace: CSS, kind: properties
a Java class: namespace: Java, kind: classes
a plugin extension: namespace: ij-plugin, kind: extensions
A Web Symbol can originate from source code analysis, or it can be a symbol statically defined through Web Types (Web Types) (JSON) or some other custom format. In both cases, such a symbol can have some source defined. Each symbol is treated by the framework the same, regardless of their origin.
WebSymbol has a number of properties which are used across IDE features:
Describes which language or concept the symbol belongs to.
Describes which group of symbols within the particular language or concept (namespace) the symbol belongs to. The kind should be plural in form, e.g. "attributes".
The name of the symbol. If the symbol does not have a pattern, the name will be used as-is for matching.
Specifies where this symbol comes from. Besides descriptive information like framework, library, version, or default icon, it also provides an interface to load symbol types and icons.
An optional icon associated with the symbol, which is going to be used across the IDE. If none is specified, a default icon of the origin will be used and if that’s not available, a default icon for symbol namespace and kind.
Symbols with higher priority will have precedence over those with lower priority, when matching is performed. Symbols with higher priority will also show higher on the completion list.
Provides an additional way to sort symbols in the code completion list within a particular priority. The value must be a non-negative integer, and the higher proximity, the higher the symbol would be on the list.
Since 2023.2 - replaces deprecated and experimental properties
Documents API status of the symbol. It is one of the sub-interfaces of WebSymbolApiStatus (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/WebSymbolApiStatus.kt): Stable, Experimental or Deprecated. Deprecated symbols are appropriately highlighted in the code editor, code completion, and quick documentation.
Removed in 2023.2 - replaced with apiStatus property
Documents, whether the symbol is deprecated. Deprecated symbols are appropriately highlighted in the code editor, code completion, and quick documentation.
Removed in 2023.2 - replaced with apiStatus property
Documents, whether the symbol is considered an experimental feature and should be used with caution and might be removed or its API altered in the future.
Whether this symbol is required. What "is required" means depends on the symbol. For instance, for an HTML attribute, it would mean that the attribute is required to be present for the particular HTML element. For JavaScript property, it would mean that it is not optional, so it cannot be undefined.
If the symbol represents some property, variable, or anything that can hold a value, this property documents what is the default value.
The type of the symbol. The type can be interpreted only within the context of symbol origin and in regard to its namespace and kind. The type may be a language type, coming from e.g., Java or TypeScript, or it may be any arbitrary value. Usually a type would be associated with symbols, which can hold a value, or represent some language symbol, like class, method, etc.
A PsiElement, which is a file or an element, which can be used to roughly locate the source of the symbol within a project to provide a context for loading additional information, like types. If the symbol is PsiSourcedWebSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/PsiSourcedWebSymbol.kt) (see ), then psiContext is equal to source.
Various symbol properties. There should be no assumption on the type of properties. Properties can be used by plugins to provide additional information on the symbol. See Web Types Special Properties ("Special Properties" in "Web Types") section for reference to the custom properties supported by IDEs.
Returns TargetPresentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/platform/backend/presentation/TargetPresentation.kt) used by SearchTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/find/usages/api/SearchTarget.kt) and RenameTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/rename/api/RenameTarget.kt). Default implementations of WebSymbolRenameTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/refactoring/WebSymbolRenameTarget.kt) and WebSymbolSearchTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/search/WebSymbolSearchTarget.kt) use the presentation property.
The following properties handle generation of Quick Doc (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation) in the IDE:
An optional text, which describes the symbol's purpose and usage. It is rendered in the documentation popup or view.
Additional sections, to be rendered in the symbols’ documentation. Each section should have a name, but the contents are optional.
An optional URL to a website with detailed symbol's documentation
Removed in 2023.1.1 - replaced by createDocumentation()
An interface holding information required to render documentation for the symbol. To customize symbols documentation, one can override the method, or implementWebSymbolDocumentationCustomizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/documentation/WebSymbolDocumentationCustomizer.kt). WebSymbolDocumentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/documentation/WebSymbolDocumentation.kt) interface provides builder methods for customizing the documentation. with* methods return a copy of the documentation with customized fields.
The following properties are related to name matching and code completion queries:
The pattern to match names against. As a result of pattern matching, a WebSymbolMatch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolMatch.kt) will be created. A pattern may specify that a reference to other Web Symbols is expected in some part of it. For such places, appropriate segments with referenced Web Symbols will be created and navigation, validation, and refactoring support are available out-of-the-box.
When a pattern is being evaluated, matched symbols can provide additional scope for further resolution in the pattern. By default, the queryScope returns the symbol itself
Some symbols represent only a framework syntax, which does not translate to a particular symbol in the runtime. For instance, a Vue directive, which needs to be prefixed with v- will result in some special code generated, but as such is not a real HTML attribute. This distinction allows us to ignore such symbols when looking for references.
Some symbols may have a lot in common with each other, and one can use abstract symbols as their super symbol. For performance reasons, only statically defined symbols (Web Types, Custom Elements Manifest (https://github.com/webcomponents/custom-elements-manifest)) can inherit from other statically defined symbols. For dynamically defined symbols, regular class inheritance should be used.
Specifies whether the symbol is an extension. When matched along with a non-extension symbol, it can provide or override some properties of the symbol, or it can extend its scope contents.
A special property to support symbols representing HTML attributes. It can specify the kind (plain, expression, no-value), type (boolean, number, string, enum, complex, of-match), whether an attribute value is required, a default value, and the result type of value expression in the appropriate language. If COMPLEX type is set, the value of langType will be used and if OF_MATCH, the type of the symbol will be used. When merging information from several segments in the WebSymbolMatch, first non-null property values take precedence. By default - when properties are null - attribute value is of plain type and is required.
Returns the pointer to the symbol, which can survive between read actions. The dereferenced symbol should be valid, e.g., any PSI-based properties should return valid PsiElements.
Symbols can be used in CachedValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/util/CachedValue.java) s as dependencies. If a symbol instance can mutate over time, it should properly implement this method.
Returns true if two symbols are the same or equivalent for resolve purposes.
Web Symbols can have various naming conventions. This method is used by the framework to determine a new name for a symbol based on its occurrence.
Since 2023.1.1
Used by the Web Symbols framework to get a DocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationTarget.kt), which handles documentation rendering for the symbol. The default implementation will use createDocumentation() to render the documentation.
Since 2023.1.1 - replaces documentation property
Returns WebSymbolDocumentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/documentation/WebSymbolDocumentation.kt) - an interface holding information required to render documentation for the symbol. By default, its contents are built from the available Web Symbol information. To customize symbols documentation, one can override the method, or implement WebSymbolDocumentationCustomizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/documentation/WebSymbolDocumentationCustomizer.kt).
WebSymbolDocumentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/documentation/WebSymbolDocumentation.kt) interface provides builder methods for customizing the documentation. with* methods return a copy of the documentation with customized fields.
A symbol should implement PsiSourcedWebSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/PsiSourcedWebSymbol.kt) if its declaration is a regular PsiElement, e.g., a variable or a declared type. Once a symbol implements this interface, it can be searched and refactored together with the PSI element declaration. In case a symbol is part of a PsiElement (for instance, being part of a string literal), spans multiple PSI elements, or does not correlate one-to-one with a PSI element, contribution of a dedicated declaration provider instead of implementing this interface is recommended.
The PsiElement, which is the symbol declaration.
WebSymbolMatch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolMatch.kt) and some special symbols can have a name, which consists of other Web Symbols.
List of WebSymbolNameSegment (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/WebSymbolNameSegment.kt). Each segment describes a range in the symbol name. Segments can be built of other Web Symbols and/or have related matching problems - missing required part, unknown symbol name or be a duplicate of another segment. See the Model Queries Example section for an example.
Web Symbols are contained within a loose model built from Web Symbols scopes, each time anew for a particular context. Each Web Symbol is also a WebSymbolsScope (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/WebSymbolsScope.kt) and it can contain other Web Symbols. For instance, an HTML element symbol would contain some HTML attributes symbols, or a JavaScript class symbol would contain field and method symbols. When configuring queries, Web Symbols scopes are added to the list to create an initial scope for symbols resolve.
Returns symbols within the scope. If provided name is null, no pattern evaluation will happen and all symbols of a particular kind and from a particular namespace will be returned.
Returns code completions for symbols within the scope.
When scope is exclusive for a particular namespace and kind, resolve will not continue down the stack during pattern matching.
Returns the pointer to the symbol scope, which can survive between read actions. The dereferenced symbol scope should be valid.
Symbol scopes are used in CachedValues as dependencies for query executors. If a symbol scope instance can mutate over time, it should properly implement this method.
When implementing a scope containing many elements, an extension of WebSymbolsScopeWithCache (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/WebSymbolsScopeWithCache.kt) is advised. This structure caches the list of symbols and uses an efficient cache mechanism to speed up queries. On extension of this class, it's only necessary to override initialize() and provide parameters to the super constructor to specify the caching strategy for the results.
Web Symbols can contain patterns, which allow to compose them from other Web Symbols. To find which symbols match available patterns, we need to make a match query. One can also run a code completion query, which will produce a list of valid completions in the provided context.
To perform a query, create a WebSymbolsQueryExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryExecutor.kt) using WebSymbolsQueryExecutorFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryExecutorFactory.kt). The query executor will be configured by all the registered WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt) 's based on the provided PSI context. Configurators will provide initial Web Symbol scopes, rules for calculating Web Symbols context, and rules for symbol names conversion.
The result of the match query is a list of WebSymbols. Some of them might be WebSymbolMatch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolMatch.kt) es. Such objects represent complex matches when patterns are used. Web Symbol Match has nameSegments property, which precisely describes how segments of the name relate to referenced Web Symbols and whether there are any problems with resolution or the name itself.
When working with code completion, one can query for the list of code completions. To properly calculate completions, a position in the current text under completion is required. As a result, a list of WebSymbolCodeCompletionItem (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/completion/WebSymbolCodeCompletionItem.kt) will be provided.
Let’s take a Vue directive as an example. It is a special HTML attribute processed by the Vue framework in runtime or during compile, which results in additional code being attached to the DOM element. Its structure looks as follows:
An example of how Vue directive might be declared in Web Types is here. Once a match query is run on v-on:click.once.alt, we will get a WebSymbolMatch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolMatch.kt) with the following segments:
v-: Vue directive pattern symbol
on: Vue on directive
:
click: DOM click event symbol
.
once: Vue on directive once modifier
alt: Vue on directive alt modifier
Usually one would create such elements using Web Types, but sometimes there might be a need to do that programmatically.
To simplify resolution and make it less ambiguous, a segment to match is selected by taking everything up to static prefixes of the following patterns. Thus, if we want to have symbol references and regular expressions in the pattern, they either have to terminate the pattern or must be followed by a static text. A regular pattern static prefix is also considered a static text.
There are seven types of patterns:
String match: try to match an exact text, the match is case-sensitive
Regular expression match: try to match a regular expression, the match can be case-insensitive
Symbol reference placeholder: a symbol reference resolve will be attempted when this pattern is reached. A resolve will be made by the symbols provider from an enclosing complex pattern. If none of the symbols match the segment, the segment will have UNKNOWN_SYMBOL problem reported. The matched symbol might be a WebSymbolMatch itself, which allows for nesting patterns.
Pattern sequence: a sequence of patterns. If some patterns are not matched, an empty segment with MISSING_REQUIRED_PART will be created.
Complex pattern: this pattern is called complex, because it makes several things:
The provided patterns are treated as alternatives.
It can have symbols resolver, which is used by nested symbol reference placeholder patterns.
It allows adding an additional scope to resolve stack
A complex pattern might be optional, in which case its absence is not reported as an error in an enclosing sequence or complex pattern
The match can be repeated, and any duplicate segments might have DUPLICATED problem set
It can override proximity and priority, which by default is based on priority and proximity of matched symbols.
Completion auto popup: a special pattern, which works only in code completion queries. It delimits the place where when creating code completion items, pattern evaluation should be stopped and ... added. Selecting such items will result in adding the prefix part, and then another code completion popup will open. The pattern can be sticky, which means that the prefix will be shown in the nested code completion list.
Single symbol reference (since 2023.2): try to match text against the symbol name, but put a reference to another element.
When performing queries, some symbols should be excluded and others included in particular contexts. For instance, if we have an Angular project, none of the Vue components should be available. WebSymbolsContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/context/WebSymbolsContext.kt) is created using rules provided by WebSymbolsQueryConfigurators with the addition of custom WebSymbolsContextProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/context/WebSymbolsContextProvider.kt). As a result, for each kind of context, there is at most a single name assigned. WebSymbolsContext can also be used outside the WebSymbolsQueryExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryExecutor.kt) as an efficient way to determine whether to enable or disable particular functionality in the IDE based on PSI or VFS context.
The stack is used as a scope for resolving symbols. All scopes provided by WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt) s together with the list of additional scopes passed as arguments to the query create an initial query stack. Each time a symbol is matched, the list returned by queryScope property is added to the stack for any subsequent matches further right the pattern.
To provide locations of declarations of Web Symbols, which are not PsiSourcedWebSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/PsiSourcedWebSymbol.kt) s, a dedicated WebSymbolDeclarationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/declarations/WebSymbolDeclarationProvider.kt) should be registered. It should return a list of WebSymbolDeclaration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/declarations/WebSymbolDeclaration.kt) s in a particular PsiElement at a particular offset.
Similarly, to provide references, a PsiSymbolReferenceProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/model/psi/PsiSymbolReferenceProvider.java) should be registered. It should return WebSymbolReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/references/WebSymbolReference.kt) objects from PsiSymbolReferenceProvider.getReferences().
To support search/finding usages, Web Symbol needs to implement SearchTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/find/usages/api/SearchTarget.kt) or a WebSymbolSearchTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/search/WebSymbolSearchTarget.kt) needs to be provided for it through a SymbolSearchTargetFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/find/usages/symbol/SymbolSearchTargetFactory.java).
To support name refactoring, the RenameTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/rename/api/RenameTarget.kt) interface needs to be implemented, or a WebSymbolRenameTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/refactoring/WebSymbolRenameTarget.kt) needs to be provided for it through a SymbolRenameTargetFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/rename/symbol/SymbolRenameTargetFactory.java).
IDEs provide built-in support for Web Symbols integration with main language features of HTML, CSS, and JavaScript. Contribution of static symbols can be achieved through Web Types or Custom Elements Manifest, or by creating and registering a WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt) extension.
The implementation of WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt) requires overriding getScope(). This process involves the creation of a WebSymbolScope list, depending on the provided PsiElement (element parameter) and WebSymbolContext (context parameter). The following list of supported language features outlines the types of PsiElements that can be expected for each supported language feature.
Namespace: html
Kind: elements
Web Symbols representing available HTML elements. HTML element symbols can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
HtmlTag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/html/HtmlTag.java) - Web Symbols should represent available HTML elements. The HTML tag's actual name should not be taken into account when building the scope.
CssElement - Web Symbols should represent available HTML elements within a particular CSS file.
The matched Web Symbol is taken as a scope for matching HTML attributes of the tag.
Namespace: html
Kind: attributes
Web Symbols representing available HTML attributes. HTML attribute symbols can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file. If the containing tag is matched to a Web Symbol, it is added to the scope for attribute matching.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
XmlAttribute (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlAttribute.java) - Web Symbols should represent available HTML attributes for matching. The HTML attribute actual name should not be taken into account when building the scope. However, the parent HTML tag and other attributes can be taken into account when building the scope.
HtmlTag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/html/HtmlTag.java) - Web Symbols should represent available HTML attributes for matching. The HTML tag and other attributes can be taken into account when building the scope.
Dedicated support for WebSymbol interface properties:
required - if true, a warning will be shown if the attribute is missing. Does not apply to virtual attributes.
default - the default value of the attribute, if attributeValue.default is null.
attributeValue - provides information about the attribute value, see attributeValue ("HTML support" in "Implementing Web Symbols") reference.
Namespace: css
Kind: properties
Web Symbols representing available CSS properties. Custom CSS properties (variables) (https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) names should be prefixed with --. CSS properties can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
Within a CSS file, the additional scope for matching properties is built from Web Symbols matching HTML element names from terminal selectors from the enclosing ruleset.
Within an HTML element style attribute value, an additional scope is built from Web Symbols matching the enclosing HTML element.
Scope for custom properties (variables) references (arguments for var() (https://developer.mozilla.org/en-US/docs/Web/CSS/var) function) is built the same way as for properties. Only properties with names starting with -- are taken into account.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssDeclaration - Web Symbols should represent available CSS properties for matching. The CSS declaration actual name should not be taken into account when building the scope.
Namespace: css
Kind: pseudo-elements
Web Symbols representing available CSS pseudo-elements (https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements). Symbols names should not be prefixed with ::. CSS pseudo-elements can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
The additional scope for matching pseudo-elements is built from Web Symbols matching HTML element name preceding the pseudo-element keyword.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssPseudoSelector - Web Symbols should represent available CSS pseudo-elements for matching. The CSS pseudo-element actual name should not be taken into account when building the scope.
Dedicated support for WebSymbol interface properties:
properties[WebSymbol.PROP_ARGUMENTS] - true if pseudo-element keyword accepts arguments.
Namespace: css
Kind: pseudo-classes
Web Symbols representing available CSS pseudo-classes (https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes). Symbols names should not be prefixed with :. CSS pseudo-classes can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
The additional scope for matching pseudo-classes is built from Web Symbols matching HTML element name preceding the pseudo-class keyword.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssPseudoSelector - Web Symbols should represent available CSS pseudo-classes for matching. The CSS pseudo-class actual name should not be taken into account when building the scope.
Dedicated support for WebSymbol interface properties:
properties[WebSymbol.PROP_ARGUMENTS] - true if pseudo-class keyword accepts arguments.
Namespace: css
Kind: functions
Web Symbols representing available CSS functions (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions). CSS functions can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
The additional scope for matching functions is built from Web Symbols matching the CSS property name, the value of which is being calculated.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssFunction - Web Symbols should represent available CSS functions for matching. The CSS function actual name should not be taken into account when building the scope.
Namespace: css
Kind: classes
Web Symbols representing available CSS classes. Symbols names should not be prefixed with .. CSS classes can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
Within CSS file, an additional scope for matching classes is built from Web Symbols matching HTML element name preceding the class keyword.
Within HTML attribute class the additional scope for matching classes is built from Web Symbols matching the enclosing HTML element name.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssClass - Web Symbols should represent available CSS classes for matching. The CSS class actual name should not be taken into account when building the scope.
XmlAttributeValue (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/xml/XmlAttributeValue.java) - Web Symbols should represent available CSS classes for matching within the attribute value.
2023.2+
Namespace: css
Kind: parts
Web Symbols representing available HTML element parts for matching with CSS ::part (https://developer.mozilla.org/en-US/docs/Web/CSS/::part) pseudo-element. CSS parts can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
Within CSS file, an additional scope for matching classes is built from Web Symbols matching HTML element name preceding the ::part keyword.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
CssTermImpl - Web Symbols should represent available CSS parts for matching. The CSS part's actual name should not be taken into account when building the scope.
2023.2+
Namespace: js
Kind: string-literals
Web Symbols representing JavaScript or TypeScript string literals available in a particular location. Only dynamically contributed string literal symbols (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)) have built-in support.
The WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
a JSLiteralExpression
an unqualified JSReferenceExpression, which parent is not JSIndexedPropertyAccessExpression, JSCallExpression or JSProperty
2023.2+
Namespace: js
Kind: properties
Web Symbols represent properties of an object, which is a result of a JavaScript or TypeScript expression. Only dynamically contributed properties symbols (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)) have built-in support.
The WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
JSObjectLiteralExpression - Web Symbols should represent expected properties
JSExpression - Web Symbols should represent available properties the expression's result. Parent expression is JSReferenceExpression or JSIndexedPropertyAccessExpression.
Dedicated support for WebSymbol interface properties:
type - if the type is JSType, it will be used in JavaScript type evaluator as the type of the property
required - the JavaScript property is treated as non-optional
properties[WebSymbol.PROP_READ_ONLY] - the JavaScript property is treated as read-only
2023.2+
Namespace: js
Kind: symbols
Web Symbols representing JavaScript symbols available for resolve of an unqualified JavaScript reference expression. Only dynamically contributed symbols (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)) have built-in support.
The WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
an unqualified JSReferenceExpression - Web Symbols should represent possible symbols to which JavaScript reference could resolve. The reference to actual name should not be taken into account when building the scope.
Dedicated support for WebSymbol interface properties:
type - if the type is JSType, it will be used in JavaScript type evaluator as the type of the symbol
properties[WebSymbol.PROP_KIND] - the kind of the symbol, one of WebSymbolJsKind (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/js/WebSymbolJsKind.kt) enum values. The kind will be used to render the appropriate icon in the code completion popup
Integration with unqualified reference resolution is not available in TypeScript code
Namespace: js
Kind: events
Web Symbols representing available DOM events. DOM events can be contributed statically or dynamically (through WebSymbolsQueryConfigurator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/query/WebSymbolsQueryConfigurator.kt)). The statically contributed symbols are available globally, depending on the context setting of the contributing Web Types file.
The additional scope for matching DOM events is built from Web Symbols matching enclosing HTML element name.
For dynamic contributions, the WebSymbolsQueryConfigurator.getScope()'s element parameter can be:
HtmlTag (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-api/src/com/intellij/psi/html/HtmlTag.java) - Web Symbols should represent available DOM events for matching.
When a language feature, such as an HTML element, is capable of representing an entity from a framework, like a Vue component, it may be beneficial to use a custom symbol kind with a domain-specific name (for example, vue-components), and establish a mapping from this to the language feature via a pattern:
{
"contributions": {
"html": {
"elements": [
{
"name": "Vue component",
"pattern": {
"items": "/html/vue-components"
},
"attributes": [
{
"name": "Component property",
"pattern": {
"or": [
{
"items": "props",
"priority": "highest"
}
]
},
"value": {
"type": "of-match"
}
}
]
}
]
}
}
}Note that in this example, attributes are produced from html/props symbols. For Vue component, a set of named values, which can be provided to the component instance are called props. Now, our Vue component definition can have a more domain-specific look:
{
"contributions": {
"html": {
"vue-components": [
{
"name": "MyVueComponent",
"description": "This is the component you always needed in your application",
"props": [
{
"name": "listen-to",
"type": "string | HTMLElement | Document | Window | (() => HTMLElement)",
"description": "The scrolling element to listen to.",
"default": "document"
}
]
}
]
}
}
}Web Types is a JSON metadata format, which provides an easy way to contribute statically defined Web Symbols. The JSON Web Types detailed schema can be accessed by following this link (https://github.com/JetBrains/web-types/blob/master/schema/web-types.json). The format is open source and IDE-agnostic by itself. However, currently it is being actively used mostly by JetBrains IDEs.
Originally, it was created to facilitate the contribution of statically defined symbols for the Vue (https://vuejs.org/) framework, which may explain the presence of some deprecated properties in the schema.
A simple Web Types file looks as follows, where this file defines a my-element HTML element with a foo attribute:
{
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
"name": "example",
"version": "0.0.1",
"description-markup": "markdown",
"contributions": {
"html": {
"elements": [
{
"name": "my-element",
"description": "A custom HTML element",
"doc-url": "https://example.com/docs/my-element",
"attributes": [
{
"name": "foo",
"description": "A custom attribute of `my-element`"
}
]
}
]
}
}
}The Web Types file should, at least, contain name, version and contributions properties. It should also include $schema property which can be either https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json or http://json.schemastore.org/web-types. The schema contains detailed documentation for all the JSON entities.
Directly under contributions property are listed namespaces with their contributions. Currently only html, css or js namespaces are allowed. However, in the future this limitation will be lifted to support Web Types for other technologies.
The namespace object contains symbol kind names listed as properties. Some symbol kinds are predefined and directly supported by IDE (see for reference). The kind of symbol should be related to its role. For instance, a Vue directive should be of the kind vue-directives. Each framework should define a set of custom symbol kinds if needed. Reference for the most important symbol kinds defined by frameworks supported by IDEs is below.
Each symbol kind name property of a namespace object is an array of symbol contributions. A symbol contribution should have at least a name. Contributions in addition to standard properties can define sub-contributions and custom properties. A custom property is a JSON property, whose value is of a simple type (string, number or boolean), or is an array of simple types. If a contribution’s JSON property’s value is an object value or an array of objects, it is treated as a list of sub-contributions. Such contributions will be assigned to the same namespace as the parent contributions. To use a different namespace for sub-contributions, nest symbol kind JSON property name under a js, css or html property, e.g.:
{
"contributions": {
"html": {
"elements": [
{
"name": "my-element",
"description": "A custom HTML element",
"attributes": [
{
"name": "foo",
"description": "A custom HTML attribute of `my-element`"
}
],
"css": {
"properties": [
{
"name": "--bg-color",
"description": "Background color of my-element"
}
]
}
}
]
}
}
}In the example below, Web Types contributes information that the my-element HTML element supports a custom CSS property --bg-color. The attributes are implicitly under the html namespace. To contribute a foo attribute, one could also write it in longer form:
{
"name": "my-element",
"description": "A custom HTML element",
"html": {
"attributes": [
{
"name": "foo",
"description": "A custom attribute of `my-element`"
}
]
}
}Each Web Types contribution is represented in the Web Symbols framework by a PsiSourcedWebSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/src/com/intellij/webSymbols/PsiSourcedWebSymbol.kt) object. All the Web Types contributions are mapped one-to-one, and custom properties are accessible through properties property.
Web Types can currently be discovered by the IDE in the following ways:
The IDE will automatically discover any Web Types shipped with the NPM library and specified in the web-types property of package.json. The property accepts a string or an array of strings with relative paths to Web Types files shipped with the package.
In your JavaScript projects in package.json files, you can specify web-types property similarly to the NPM package. The property accepts a string or an array of strings with relative paths to Web Types files within the project.
You can ship Web Types JSON with your IDE plugin. To point an IDE to its location, use com.intellij.webSymbols.webTypes extension point and pass the file location in source attribute value. With enableByDefault attribute, you can choose whether the Web Types file should be contributed to Web Symbols scope by default, or only if an NPM package with the same name is present in the project.
Supported by html/elements and html/attributes, allows to inject the specified language into HTML element text or HTML attribute value.
If a symbol uses a RegEx pattern, usually it will be displayed in a documentation popup section "pattern". Setting this property to true hides that section.
By default, all symbols show up in code completion. Setting this property to true prevents a symbol from showing up in the code completion.
Web Types files are used internally by IDEs and tools to define rules for frameworks. Following is the reference for symbol kinds used by framework support.
IDEs provide direct support for the following symbol kinds:
html/elements
html/attributes
css/properties
css/pseudo-classes
css/pseudo-elements
css/functions
css/classes
css/parts (since 2023.2)
Prior to 2023.1, IDEs were required to have JavaScript plugin installed for the support to work.
Angular plugin Web Types are available here (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/Angular/resources/web-types) for reference. Any Web Types file targeting only Angular support should have framework property set to angular. Highlights: js/ng-custom-events contribute symbols with patterns for any custom events supported by Angular EventManagers, e.g.:
{
"ng-custom-events": [
{
"name": "Custom modifiers for declarative events handling",
"priority": "normal",
"pattern": {
"template": [
{
"items": {
"path": "/js/events",
"includeVirtual": false
}
},
{
"items": "ng-event-plugins-modifiers",
"template": [
".",
"#...",
"#item:modifiers"
],
"priority": "high",
"repeat": true,
"unique": true,
"required": false
}
]
},
"ng-event-plugins-modifiers": [
{
"name": "prevent"
},
{
"name": "stop"
}
]
}
]
}Vue plugin Web Types are available here (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/vuejs/resources/web-types) for reference. Any Web Types file targeting only Vue support should have framework property set to vue. Highlights:
Use /html/vue-components to contribute Vue components.
Use /html/vue-directives to contribute Vue directives. Use attribute-value property to specify the type of value expression. E.g.:
{
"attribute-value": {
"type": "boolean",
"required": true
}
}Use /html/vue-file-top-elements to contribute any custom top-level elements available in Vue Single Component File
A Vue /html/vue-components contribution supports:
Use /html/props to contribute Vue component props, e.g:
{
"props": [{
"name": "appear",
"description": "Whether to apply transition on initial render.",
"type": "boolean",
"default": "false"
}]
}Use /html/slots to contribute Vue component slots. For scoped slots, use vue-properties to provide a list of scoped slot properties. Example:
{
"slots": [{
"name": "img",
"description": "Expects the [v-img](/components/images) component.",
"doc-url": "https://vuetifyjs.com/en/api/v-app-bar/#slots",
"vue-properties": [
{
"name": "props",
"type": "{ height: string, src: string | srcObject }"
}
]
}]
}Use /js/events to contribute Vue component events, e.g.:
{
"js": {
"events": [
{
"name": "input",
"description": "The updated bound model"
}
]
}
}Use html/vue-model to contribute settings for Vue model directive. E.g.:
{
"vue-model": {
"prop": "show",
"event": "input"
}
}A Vue /html/vue-directives contribution supports:
Use /html/argument as a Vue directive argument. E.g.:
{
"argument": {
"name": "attribute or property name",
"description": "Optional attribute or property name",
"pattern": {
"items": [
{
"path": "/html/attributes",
"includeVirtual": false
}
]
}
}
}Use /html/modifiers as a Vue directive modifier. E.g.:
{
"modifiers": [
{
"name": "body",
"description": "Make the mask append to the body element",
"type": "boolean"
},
{
"name": "fullscreen",
"type": "boolean"
}
]
}For Lit support, install it in your Node project as a devDependency @web-types/lit. Web Types are available here (https://github.com/JetBrains/web-types/blob/master/packages/lit/lit%402.0.0/lit.web-types.json) for reference.
Web Components should use:
/html/attributes: for attributes available in HTML
/js/properties: for Web Component DOM class properties
/js/events: for Web Component events
Example Web Component:
{
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
"name": "Cool library",
"version": "1.0.0",
"js-types-syntax": "typescript",
"description-markup": "markdown",
"contributions": {
"html": {
"elements": [
{
"name": "cool-component",
"description": "Use the cool component to make your website more attractive.",
"doc-url": "https://example.com/docs/cool-component",
"attributes": [
{
"name": "color",
"description": "Choose color for coolness",
"default": "blue",
"required": false,
"doc-url": "https://example.com/docs/cool-component#attrs",
"value": {
"type": "string"
}
}
],
"slots": [
{
"name": "container"
}
],
"events": [
{
"name": "color:changed",
"description": "Emitted when color changes"
}
],
"js": {
"properties": [
{
"name": "color",
"type": "string",
"default": "blue"
}
]
},
"css": {
"properties": [
{
"name": "--cool-degree"
}
]
}
}
]
}
}
}This API is available starting from 2020.3 and currently in development and thus in experimental state.
The Navigate | Declaration or Usages action is performed in several steps.
Direct navigation is the navigation from PsiElement to another PsiElement, such as navigation from break keyword to the end of a loop in Java, without showing any popups.
To provide PsiElement for direct navigation, implement and register DirectNavigationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/navigation/DirectNavigationProvider.java) in com.intellij.lang.directNavigationProvider EP.
If there is no Direct navigation available under the caret, then the IntelliJ Platform proceeds with Symbol navigation. In this step the IntelliJ Platform computes the navigation targets based on target symbols, which it obtains by resolving a reference ("References" in "Declarations and References"). If there are several target symbols or several navigation targets defined for a symbol, then the IDE shows the navigation popup to ask the user to choose where to go.
The NavigationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/platform/backend/navigation/NavigationTarget.java) is essentially a pair of a Navigatable and a TargetPresentation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/platform/backend/presentation/TargetPresentation.kt) instances (where to go and what to show in the popup).
To provide navigation targets by a Symbol, either:
implement and register SymbolNavigationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/navigation/SymbolNavigationProvider.java) in com.intellij.symbolNavigation EP;
or implement NavigatableSymbol (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/navigation/NavigatableSymbol.java) in the Symbol.
If there are no navigation targets available, then the IntelliJ Platform starts finding usages of the target symbol obtained by resolving a reference ("References" in "Declarations and References") or from a declaration ("Declarations" in "Declarations and References").
Existing implementations of the mentioned extension points can be found on the IntelliJ Platform Explorer ("3.2 Search the IntelliJ Platform Explorer" in "Explore the IntelliJ Platform API").
Product Help: Code completion (https://www.jetbrains.com/help/idea/auto-completing-code.html)
Two main types of code completion can be provided by custom language plugins: reference completion and contributor-based completion.
Reference completion is easier to implement, but supports only the basic completion action. Contributor-based completion provides more features, supports all three completion types (basic, smart, and class name), and can be used, for example, to implement keyword completion.
To fill the completion list, the IDE calls PsiReference.getVariants() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) either on the reference at the caret location or on a dummy reference that would be placed at the caret. This method needs to return an array of objects containing either strings, PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) instances or instances of the LookupElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupElement.java) class (see Lookup Items below). If a PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) instance is returned in the array, the completion list shows the icon for the element.
The most common way to implement getVariants() is to use the same function for walking up the tree as in PsiReference.resolve() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java), and a different implementation of PsiScopeProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/scope/PsiScopeProcessor.java) which collects all declarations passed to its execute() method and returns them as an array for filling the completion list.
This API is available starting from 2020.3 and currently in development and thus in experimental state.
To provide completion variants by a PsiSymbolReference implement PsiCompletableReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/model/psi/PsiCompletableReference.java).
Implementing the CompletionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionContributor.java) interface gives you the greatest control over the operation of code completion for your language. Register in com.intellij.completion.contributor extension point and specify language attribute (unless it works on any supported language).
Note that the Javadoc of that class contains a detailed FAQ for implementing code completion.
The core scenario of using CompletionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionContributor.java) consists of calling the extend() method and passing in the Element Pattern (Element Patterns) specifying the context in which this completion variant is applicable, as well as a completion provider which generates the items to show in the completion list.
Keep in mind that the pattern is checked against the leaf PSI element. If you want to match a composite element, use withParent() or withSuperParent() methods.
If completion items do not depend on indexes (e.g., keywords), it can be marked as dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
Examples:
CompletionContributor (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/osmorc/src/org/osmorc/manifest/completion/OsgiManifestCompletionContributor.java) for completing keywords in MANIFEST.MF files.
Custom Language Support Tutorial: Completion Contributor (9. Completion Contributor)
Items shown in the completion list are represented by instances of the LookupElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupElement.java) interface. These instances are typically created through the LookupElementBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupElementBuilder.java) class.
For every lookup element, you can specify the following attributes:
Text Shown left-aligned.
Tail text Shown next to the main item text, is not used for prefix matching, and can be used, for example, to show the parameter list of the method.
Type text Shown right-aligned in the lookup list and can be used to show the return type or containing class of a method, for example.
Icon
Text attributes Bold, Strikeout, etc.
Insert handler The insert handler is a callback which is called when the item is selected and can be used to perform additional modifications of the text (for example, to put in the parentheses for a method call)
Use AutoPopupController.scheduleAutoPopup() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/codeInsight/AutoPopupController.java).
Use LookupListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/lookup/LookupListener.java) to receive notifications about completion popup lifecycle events.
Product Help: Find Usages (https://www.jetbrains.com/help/idea/find-highlight-usages.html)
The Find Usages action is a multi-step process, and each step of the process requires involvement from the custom language plugin.
The language plugin participates in the Find Usages process by registering an implementation of FindUsagesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java) in the com.intellij.lang.findUsagesProvider extension point, and through the PSI implementation using PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) and PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) interfaces.
In cases like function parameters and local variables, consider overriding PsiElement.getUseScope() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) to return a narrower scope. For instance, returning the scope of the nearest function definition can significantly reduce the number of files that need to be parsed and references that need to be resolved when renaming such elements.
The steps of the Find Usages action are the following:
Before the Find Usages action can be invoked, the IDE builds an index of words present in every file in the custom language. Using the WordsScanner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/cacheBuilder/WordsScanner.java) implementation returned from FindUsagesProvider.getWordsScanner() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java), the contents of every file are loaded and passes it to the words scanner, along with the words consumer. The words scanner breaks the text into words, defines the context for each word (code, comments, or literals), and passes the word to the consumer. The simplest way to implement the words scanner is to use the DefaultWordsScanner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java) implementation, passing to it the sets of lexer token types which are treated as identifiers, literals, and comments. The default words scanner will use the lexer to break the text into tokens and handle breaking the text of the comment and literal tokens into individual words.
When the user invokes the Find Usages action, the IDE locates the PSI element the references to be searched. The PSI element at the cursor (the direct tree parent of the token at the cursor position) must be either a PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) or a PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) which resolves to a PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java). The word cache will be used to search for the text returned from the PsiNamedElement.getName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) method. Also, if the text range of the PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) includes some other text besides the identifier returned from getName() (for example, if the PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) represents a JavaScript function and its text range includes the " function " keyword in addition to the name of the function), the method getTextOffset() must be overridden for the PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java), and must return the start offset of the name identifier within the text range of the element.
Once the element is located, the IDE calls FindUsagesProvider.canFindUsagesFor() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java) to ask the plugin if the Find Usages action applies to the specific element.
When showing the Find Usages dialog to the user, FindUsagesProvider.getType() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java) and FindUsagesProvider.getDescriptiveName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java) are called to determine how the element should be presented to the user.
For every file containing the searched words, the IDE builds the PSI tree and recursively descends it. The text of each element is broken into words and then scanned. If the element was indexed as an identifier, every word is checked to be a PsiReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) resolving to the element the usages of which are searched. If the element was indexed as a comment or literal and the search in comments or literals is enabled, it checks if the word is equal to the searched element's name.
After the usages are collected, results are shown in the usages pane. The text shown for each found element is taken from the FindUsagesProvider.getNodeText() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java) method. To group results by type, implement UsageTypeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView-impl/src/com/intellij/usages/impl/rules/UsageTypeProvider.java) and register in com.intellij.usageTypeProvider extension point to provide custom or predefined UsageType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView/src/com/intellij/usages/impl/rules/UsageType.java).
Examples:
Implementation of FindUsagesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/findUsages/PropertiesFindUsagesProvider.java) in Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Find Usages (11. Find Usages Provider)
To have the title of the found element be correctly displayed in the title of the Find Usages tool window, you need to provide an implementation of the ElementDescriptionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/ElementDescriptionProvider.java) interface. The ElementDescriptionLocation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/ElementDescriptionLocation.java) passed to the provider in this case will be an instance of UsageViewLongNameLocation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView/src/com/intellij/usageView/UsageViewLongNameLocation.java).
Example: ElementDescriptionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/PropertiesDescriptionProvider.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Product Help: Rename refactorings (https://www.jetbrains.com/help/idea/rename-refactorings.html)
The Rename refactoring operation is quite similar to that of Find Usages (Find Usages). It uses the same rules for locating the element to be renamed and the same index of words for finding the files that may have references to the element being renamed.
When the rename refactoring is performed, the method PsiNamedElement.setName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) is called for the renamed element, and PsiReference.handleElementRename() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReference.java) is called for all references to the renamed element. These methods perform basically the same action: replace the underlying AST node of the PSI element with the node containing the new text entered by the user. Creating an entirely correct AST node from scratch is quite tricky. Thus, surprisingly, the easiest way to get the replacement node is to create a dummy file in the custom language so that it would contain the necessary node in its parse tree, build the parse tree and extract the required node from it.
Examples:
setName() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertyImpl.java) implementation for a Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Reference Contributor (10. Reference Contributor)
If a renamed reference extends PsiReferenceBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReferenceBase.java), renaming is performed by invoking the ElementManipulator.handleContentChange() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/ElementManipulator.java), responsible for handling the content change and calculating the text range of reference inside the element.
To disable renaming for specific elements, implement com.intellij.openapi.util.Condition<T> for PsiElement of type T and register it in com.intellij.vetoRenameCondition extension point.
NamesValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/refactoring/NamesValidator.java) allows a plugin to check if the name entered by the user in the Rename dialog is a valid identifier (and not a keyword) according to the custom language rules. If an implementation of this interface is not provided by the plugin, Java rules for validating identifiers are used. Implementations of NamesValidator are registered in the com.intellij.lang.namesValidator extension point.
Example: PropertiesNamesValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/PropertiesNamesValidator.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Another way to check is RenameInputValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidator.java), unlike NamesValidator it allows you to more flexibly check the entered name for correctness based on the rule defined in the isInputValid() method.
To determine which elements this validator will apply to, override the getPattern() method returning the pattern of the element to validate.
Example: YAMLAnchorRenameInputValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/yaml/src/org/jetbrains/yaml/resolve/YAMLAnchorRenameInputValidator.java) validating YAML language anchor names
RenameInputValidator can be extended to RenameInputValidatorEx (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/refactoring/rename/RenameInputValidatorEx.java) to override the default error message. The getErrorMessage() method should return a custom error message in case of an invalid name, or null otherwise.
Note that getErrorMessage() only works if all RenameInputValidator accept the new name in isInputValid() and the name is a valid identifier for the language of the element.
Example: YamlKeyValueRenameInputValidator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/yaml/src/org/jetbrains/yaml/refactoring/rename/YamlKeyValueRenameInputValidator.java) validating YAML language keys
Implementations of RenameInputValidator or RenameInputValidatorEx are registered in the com.intellij.renameInputValidator extension point.
Further customization of the Rename refactoring processing is possible on multiple levels. Providing a custom implementation of the RenameHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/refactoring/rename/RenameHandler.java) interface allows you to entirely replace the UI and workflow of the rename refactoring, and also to support renaming something which is not a PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) at all.
Example: RenameHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-resource-bundle-editor/src/com/intellij/lang/properties/refactoring/rename/ResourceBundleFromEditorRenameHandler.java) for renaming a resource bundle in the Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
If you're okay with the standard UI but need to extend the default logic of renaming, you can provide an implementation of the RenamePsiElementProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/rename/RenamePsiElementProcessor.java) interface. This allows you to:
Rename an element different from the one on which the action was invoked (a super method, for example)
Rename multiple elements at once (if their names are linked according to the logic of your language)
Check for name conflicts (existing names, etc.)
Customize how a search for code references or text references is performed
etc.
Example: RenamePsiElementProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/refactoring/rename/RenamePropertyProcessor.java) for renaming a property in Properties plugin language (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Product Help: Safe delete (https://www.jetbrains.com/help/idea/safe-delete.html)
The Safe Delete refactoring also builds on the same Find Usages framework as Rename Refactoring.
In addition to that, to support Safe Delete, a plugin needs to implement two things:
The RefactoringSupportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java) interface, registered in the com.intellij.lang.refactoringSupport extension point, and the isSafeDeleteAvailable() method, which checks if the Safe Delete refactoring is available for a specific PSI element
The PsiElement.delete() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) method for the PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java) subclasses for which Safe Delete is available. Deleting PSI elements is implemented by deleting the underlying AST nodes from the AST tree (which, in turn, causes the text ranges corresponding to the AST nodes to be deleted from the document).
Example: delete() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/psi/impl/PropertyImpl.java) implementation for a Property in Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
If needed, it's possible to further customize how Safe Delete is performed for a particular type of element (e.g., how references are searched) via SafeDeleteProcessorDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/refactoring/safeDelete/SafeDeleteProcessorDelegate.java).
Example: SafeDeleteProcessorDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/refactoring/PropertiesFilesSafeDeleteProcessor.java) implementation for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Product Help: Code style and formatting (https://www.jetbrains.com/help/idea/code-style.html)
The IntelliJ Platform includes a powerful framework for implementing custom language formatters. In this framework, the plugin specifies the constraints on the whitespaces between different syntax elements. The formatting engine, provided by the IDE, calculates the smallest number of whitespace modifications that need to be performed on the file to make it match the constraints.
The process of formatting a file or a file fragment consists of the following main steps:
The formatting model builder (FormattingModelBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/FormattingModelBuilder.java)), implemented by the plugin, provides a formatting model (FormattingModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/FormattingModel.java)) for the document to be formatted.
The formatting model is requested to build the structure of the file as applies to formatting, as a tree of blocks (Block (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Block.java)) with an associated indent, wrap, alignment, and spacing settings.
The formatting engine calculates the sequence of whitespace characters (spaces, tabs, and/or line breaks) that needs to be placed at every block boundary, based on the plugin's formatting model.
The formatting model is requested to insert the calculated whitespace characters at necessary positions in the file.
The structure of blocks is usually built so that it mirrors the PSI structure of the file – for example, in Java code, the top-level formatting block covers the entire file. Its children cover individual classes in the file, blocks on the next level cover methods inside classes, etc. The formatter modifies only the characters between blocks, and the tree of blocks must be built so that the bottom-level blocks cover all non-whitespace characters in the file. Otherwise, the formatter may delete the characters between blocks.
To better understand how to build the block structure, use PsiViewer ("3.1 Use Internal Mode and PsiViewer" in "Explore the IntelliJ Platform API") and inspect formatting blocks built for an existing language. To invoke PsiViewer with the possibility of inspecting Block Structure, use Tools | View PSI Structure... or Tools | View PSI Structure of Current File...:

To change the default "block name" taken from class name, return custom Block.getDebugName().
If the formatting operation does not affect the entire file (for example, if the formatter is called to format the pasted block of text), a complete tree of blocks is not built. Rather, only blocks for the text range covered by the formatting operation and their parents are built.
For every block, the plugin specifies the following properties:
The spacing (Spacing (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Spacing.java)) specifies what spaces or line breaks are inserted between the specified children of the block. The spacing object specifies the minimum and maximum number of spaces that must be placed between the specified child blocks, the minimum number of line breaks to put there, and whether the existing line breaks and blank lines should be preserved. The formatting model can also specify that the formatter may not modify the spacing between the specified blocks.
The indent (Indent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Indent.java)) specifies how the block is indented relative to its parent block. There are different modes of indenting defined by factory methods in the Indent class. The most commonly used are:
the none indent (which means the child block is not indented)
the regular indent (the child block is indented by the number of spaces specified in the Tabs and Indents | Indent code style setting)
the continuation indent (based on Tabs and Indents | Continuation indent code style setting)
If the formatting model does not specify an indent, the "continuation without first" mode is used. This default means that the first block in a sequence of blocks with that type is not indented, and the following blocks are indented with a continuation indent.
The wrap (Wrap (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Wrap.java)) specifies whether the content of the block is wrapped to the next line. Wrapping is performed by inserting a line break before the block content. The plugin can specify that a particular block is never wrapped, always wrapped, or wrapped only if it exceeds the right margin.
The alignment (Alignment (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Alignment.java)) specifies which blocks should be aligned with each other. If two blocks with the alignment property set to the same object instance are placed in different lines, and if the second block is the first non-whitespace block in its line, the formatter inserts whitespaces before the second block, so that it starts from the same column as the first one.
For each of these properties, several particular use settings exist, described in the Javadoc comments for the respective classes. See also SpacingBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/SpacingBuilder.java), which aids in building rule-based configuration.
An important special case in using the formatter is the smart indent performed when the user presses the Enter key in a source code file. To determine the indent for the new line, the formatter engine calls the method getChildAttributes() on either the block immediately before the caret or the parent of that block, depending on the return value of the isIncomplete() method for the block before the caret. If the block before the cursor is incomplete (contains elements that the user will probably type but has not yet typed, like a closing parenthesis of the parameter list or the trailing semicolon of a statement), getChildAttributes() is called on the block before the caret; otherwise, it's called on the parent block.
Code formatting can be suppressed per region via special comments (https://youtrack.jetbrains.com/issue/IDEA-56995#comment=27-605969).
Example: Custom Language Support Tutorial: Formatter (16. Formatter)
Sometimes a plugin requires performing non-whitespace character modifications like reordering methods, changing letter cases, or adding missing braces. The formatting framework provides extension points allowing to achieve these goals.
Allows executing additional processing before the actual formatting is performed. For example, it can be used to adjust the formatting range or modify the code by adding, removing, or converting elements like braces, semicolons, quotes, etc. All the introduced changes will be handled by the main formatting step.
To register a formatting pre-processor, a plugin has to provide an implementation of PreFormatProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/psi/impl/source/codeStyle/PreFormatProcessor.java) and register it in the com.intellij.preFormatProcessor extension point.
Example: JsonTrailingCommaRemover (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/json/src/com/intellij/json/formatter/JsonTrailingCommaRemover.java) removing trailing commas in JSON files
It is similar to the pre-processor but is run after the actual formatting is performed. It can be used for adding, removing, or converting elements like braces, semicolons, quotes, changing letter-cases, etc.
To register a formatting post-processor, a plugin has to provide an implementation of PostFormatProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/psi/impl/source/codeStyle/PostFormatProcessor.java) and register it in the com.intellij.postFormatProcessor extension point.
Example: TrailingCommaPostFormatProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/formatter/TrailingCommaPostFormatProcessor.kt) inserting trailing commas in Kotlin files
Allows custom languages to provide user-configurable arrangement/grouping rules for element types supported by language plugin. Rules can be refined via modifiers and name; ordering can be applied additionally. Please see Rearranger (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/psi/codeStyle/arrangement/Rearranger.java) and related for Javadoc.
To specify the default indent size for the language provided by your plugin, and to allow the user to configure the tab size and indent size, you need to implement the FileTypeIndentOptionsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/psi/codeStyle/FileTypeIndentOptionsProvider.java) interface and to register the implementation in the com.intellij.fileTypeIndentOptionsProvider extension point. The return value of createIndentOptions() determines the default indent size.
Example: Custom Language Support Tutorial: Code Style Settings (17. Code Style Settings)
Use LanguageFormattingRestriction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/lang/LanguageFormattingRestriction.java) to restrict (automatic) code formatting for given contexts.
2021.3
Register AsyncDocumentFormattingService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/service/AsyncDocumentFormattingService.java) implementation in the com.intellij.formattingService (https://jb.gg/ipe?extensions=com.intellij.formattingService) extension point to invoke external formatter instead of IDE's builtin formatter.
Example: ShExternalFormatter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/sh/core/src/com/intellij/sh/formatter/ShExternalFormatter.java) from Shell Script plugin
Product Help: Code inspections (https://www.jetbrains.com/help/idea/code-inspection.html), Intention actions (https://www.jetbrains.com/help/idea/intention-actions.html)
Platform UI Guidelines: Inspections (https://jetbrains.design/intellij/text/inspections/)
The code inspections for custom languages use the same API as all other code inspections, based on the LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java) class.
The functionality of LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java) partially duplicates that of Annotator ("Annotator" in "Syntax and Error Highlighting").
The main differences are:
supports batch analysis of code (through the Code | Inspect Code... action)
the possibility to turn off the inspection (globally or by suppressing them on various levels)
ability to configure the inspection options.
If none of that is required and the analysis only needs to run in the active editor, Annotator ("Annotator" in "Syntax and Error Highlighting") provides better performance (because it supports incremental analysis) and more flexibility for highlighting errors.
See Inspections (https://jetbrains.design/intellij/text/inspections/) topic in the IntelliJ Platform UI Guidelines on naming, writing description, and message texts for inspections.
Examples:
Code Inspections Tutorial (Code Inspections)
A simple inspection (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/codeInspection/TrailingSpacesInPropertyInspection.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Please also note important change in 2024.1, refer to "Order of Running Highlighting" in "Syntax and Error Highlighting".
A custom language plugin (Custom Language Support) providing many inspections (>100) can register the default PsiElementVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElementVisitor.java) for its language in com.intellij.inspection.basicVisitor extension point (2023.3) to optimize processing.
The code intentions for custom languages also use the standard API for intentions. The intention classes need to implement the IntentionAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionAction.java) interface and are registered using the com.intellij.intentionAction extension point.
Examples:
Code Intentions Tutorial (Intentions)
A simple intention action (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/SplitIfIntention.java) for Groovy
Custom Language Support Tutorial: Quick Fix (19. Quick Fix)
Product Help: Source file structure (https://www.jetbrains.com/help/idea/viewing-structure-of-a-source-file.html)
The Structure View implementation used for a specific file type can be customized on many levels. If a custom language plugin provides an implementation of the StructureView (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureView.java) interface, it can completely replace the standard structure view implementation with a custom user interface component. However, for most languages, this is not necessary, and the standard StructureView implementation provided by IntelliJ Platform can be reused.
To modify an existing Structure View (e.g., add/filter nodes of builtin language support), use StructureViewExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/structure-view-impl/src/com/intellij/ide/structureView/StructureViewExtension.java) registered in com.intellij.lang.structureViewExtension extension point.
The starting point for the structure view is the PsiStructureViewFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/lang/PsiStructureViewFactory.java) interface, which is registered in the com.intellij.lang.psiStructureViewFactory extension point.
Examples:
PsiStructureViewFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/structureView/PropertiesStructureViewBuilderFactory.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Structure View (14. Structure View Factory)
To reuse the IntelliJ Platform implementation of the StructureView, the plugin returns a TreeBasedStructureViewBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/TreeBasedStructureViewBuilder.java) from its PsiStructureViewFactory.getStructureViewBuilder() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/lang/PsiStructureViewFactory.java) method. As the builder model, the plugin can specify a subclass of TextEditorBasedStructureViewModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/TextEditorBasedStructureViewModel.java), and by overriding methods of this subclass, it customizes the structure view for a specific language.
Example: StructureViewModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/structureView/PropertiesFileStructureViewModel.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
The main method to override is getRoot(), which returns the instance of a class implementing the StructureViewTreeElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewTreeElement.java) interface. There exists no standard implementation of this interface, so a plugin will need to implement it completely.
The structure view tree is usually built as a partial mirror of the PSI tree. In the implementation of StructureViewTreeElement.getChildren(), the plugin can specify which of the child elements of a specific PSI tree node need to be represented as elements in the structure view. Another important method is getPresentation(), which can be used to customize the text, attributes, and icon used to represent an element in the structure view.
The implementation of StructureViewTreeElement.getChildren() needs to be matched by TextEditorBasedStructureViewModel.getSuitableClasses(). The latter method returns an array of PsiElement \-derived classes, which can be shown as structure view elements. It is used to select the Structure View item matching the cursor position when the structure view is first opened or when the Autoscroll from source option is enabled.
Example: StructureViewTreeElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/editor/PropertyStructureViewElement.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Product Help: Navigation bar (https://www.jetbrains.com/help/idea/guided-tour-around-the-user-interface.html#navigation-bar)
The navigation bar implementation is used to customize and extend the navigation bar (https://www.jetbrains.com/help/idea/guided-tour-around-the-user-interface.html#navigation-bar) structure.
The starting point for the navigation bar extension is the NavBarModelExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarModelExtension.java) interface, which is registered in the com.intellij.navbar extension point.
To reuse the IntelliJ Platform implementation, you can extend one of two classes:
DefaultNavBarExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/navigationToolbar/DefaultNavBarExtension.java)
StructureAwareNavBarModelExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/navigationToolbar/StructureAwareNavBarModelExtension.kt)
DefaultNavBarExtension is the basic implementation of the navigation bar for any files. Inherit from this class if you want to create a simple navigation bar where you can change the display of folders or files for your language.
In this case, you probably only need to override the following two methods:
getPresentableText() – returns a string representation of the navigation bar part element passed to it.
getIcon() – returns the icon for the navigation bar part passed to it.
StructureAwareNavBarModelExtension is an advanced implementation that provides the ability to display specific file elements (e.g., the name of classes, functions, etc.) in a bar e.g., the name of the class at the current caret position. Inherit from it if you want to add navigation bar support to your language with support for specific file elements.
Don't forget to implement Structure View, this is necessary to build a file structure model based on which the navigation bar displays a specific element.
In this case, you will also need to override the getLanguage() in addition to the two methods described earlier, this method returns the language instance for which this extension will work.
The adjustElement() method allows you to modify the navigation bar element. It can be used, for example, when you want to show a class in the navigation bar when the caret is located in a comment that is attached to the class.
You probably won't need to override other methods unless you want to write your own implementation of the entire NavBarModelExtension interface.
Note that the getSuitableClasses() method on the structure view model class that implements com.intellij.ide.structureView.TextEditorBasedStructureViewModel (see Structure View) must return all the element types you want to display in the navigation bar.
Example: Custom Language Support Tutorial: Structure Aware Navigation Bar (15. Structure Aware Navigation Bar)
Product Help: Surround code fragments (https://www.jetbrains.com/help/idea/surrounding-blocks-of-code-with-language-constructs.html)
To support the Surround With action, the plugin needs to register one or more implementations of the SurroundDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/surroundWith/SurroundDescriptor.java) interface in the com.intellij.lang.surroundDescriptor extension point. Each of the surround descriptors defines a possible type of code fragment that can be surrounded - for example, one surround descriptor can handle surrounding expressions, and another can handle statements. Each surround descriptor, in turn, contains an array of Surrounder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java) objects, defining specific templates which can be used for surrounding the selected code fragment (for example, Surround With if, Surround With for, and so on).
When the Code | Surround With... action is invoked, the IDE queries all surround descriptors for the language until it finds one that returns a non-empty array from its getElementsToSurround() method. Then it calls the Surrounder.isApplicable() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java) method for each surrounder in that descriptor to check if the specific template is applicable in the current context. Once the user selects a specific surrounder from the popup menu, the Surrounder.surroundElements() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/surroundWith/Surrounder.java) method is used to execute the surround action.
Example: SurroundDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/surroundWith/GroovySurroundDescriptor.java) for Groovy plugin
See the Live Templates and "Surround Postfix Templates" in "Advanced Postfix Templates" sections for information on how to support code surrounding with other IDE features.
Product Help: Searching Everywhere (https://www.jetbrains.com/help/idea/searching-everywhere.html)
A custom language plugin can provide its items to be included in the lists shown when the user chooses the Navigate | Class or Navigate | Symbol action.
Provide implementations of ChooseByNameContributorEx (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/navigation/ChooseByNameContributorEx.java) interface (separate implementations need to be provided for Class and Symbol, respectively), and register them in the com.intellij.gotoClassContributor and com.intellij.gotoSymbolContributor extension points.
Each ChooseByNameContributorEx implementation must provide the following methods:
processNames(@NotNull Processor<? super String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter)
Feeds the processor with a complete list of names available in a specified scope, which the IDE will then filter according to the text typed by the user in the dialog. Using Indexing and PSI Stubs to obtain matching candidates is highly recommended to improve performance.
processElementsWithName(String name, Processor<? super NavigationItem> processor, FindSymbolParameters parameters)
Feeds the processor with a list of NavigationItem (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/navigation/NavigationItem.java) instances (typically PsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiElement.java)) matching the given name and parameters. Processed NavigationItems specify the destinations to jump to when a specific item is selected from the list.
Example:
Custom Language Support Tutorial: Go To Symbol Contributor (13. Go To Symbol Contributor)
Product Help: Quick Documentation (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation)
Custom languages can display documentation for various constructs, such as functions, methods, classes, or others, directly within the IDE. To access the documentation, users can either select View | Quick Documentation or hover over a symbol. This will open a popup that displays type information, parameters, usage descriptions, or examples. The source of the documentation content can vary. While it is often extracted from comments in the source code (e.g., Javadoc comments), external resources, such as web pages, can also be accessed.
From IntelliJ Platform version 2023.1 onwards, plugin developers can choose to implement one of three extension points (EPs) from the new Documentation Target API based on the specific use-case. These EPs enable building documentation from offsets in the current editor ("Editor Coordinate Systems" in "2. Editor Coordinates System. Positions and Offsets"), PSI elements (PSI Elements), or Symbols (Symbols). Detailed information on implementing these EPs can be found in the section.
Targeting IDEs before 2023.1Plugins targeting versions earlier than 2023.1 must use the Documentation Provider API. Note that as long as the transition to the new API is not complete, the custom language tutorial (20. Documentation) will continue using DocumentationProvider.
Custom language developers have the flexibility to select from three distinct EPs for providing documentation to their users. To ensure clarity and avoid confusion, we provide a high-level summary of the overall approach, outlining the primary components and their interactions.
Implement one of the EPs below which should extract the necessary entity (e.g. a PSI element) for which the documentation is requested. It returns instances of DocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationTarget.kt), which will be explained in a later section.
The implementation of DocumentationTarget provides the functionality to compute the rendered documentation, its presentation in the documentation tool window (https://www.jetbrains.com/help/idea/documentation-tool-window.html), or separate hints that are displayed when hovering over code.
The rendered documentation is an instance of DocumentationResult (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationResult.kt), which wraps the documentation in HTML format but can also include images or external URLs. DocumentationResult can be used asynchronously when building the documentation would take too long and block the IDE.
Implement DocumentationTargetProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationTargetProvider.java) and register it as com.intellij.platform.backend.documentation.targetProvider extension point to build documentation for a certain offset in a PsiFile by overriding documentationTargets().
Implement PsiDocumentationTargetProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/PsiDocumentationTargetProvider.java) and register it as com.intellij.platform.backend.documentation.psiTargetProvider extension point to build documentation for PSI elements by overriding documentationTarget().
Implement SymbolDocumentationTargetProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/SymbolDocumentationTargetProvider.java) and register it as com.intellij.platform.backend.documentation.symbolTargetProvider extension point to build documentation for Symbols by overriding documentationTarget().
Each of the implementations above returns instances of DocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationTarget.kt). The main work is done in computeDocumentation() where the documentation is built from the available information. If a plugin implemented the now deprecated DocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/DocumentationProvider.java) before, then computeDocumentation() should do the work that was formerly done in DocumentationProvider.generateDoc().
In addition to showing the documentation, the computeDocumentationHint() method returns the text to be displayed when the user hovers over an element with Ctrl/Cmd pressed or when Settings | Editor | Code Editing | Show quick documentation on hover is enabled. In the old framework, this method was called DocumentationProvider.getQuickNavigateInfo().
The createPointer() method manages instance restoration and ensures access to the entity across different read actions. When implementing the createPointer() method, it is essential to handle invalidated PSI elements. Unlike PSI elements, the DocumentationTarget API does not include an isValid() method and the returned pointer is expected to be null if the instance (and all contained objects) cannot be restored. See KotlinDocumentationTarget.createPointer() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/quickDoc/KotlinDocumentationTarget.kt) as a reference.
KotlinPsiDocumentationTargetProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/quickDoc/KotlinPsiDocumentationTargetProvider.kt)
KotlinDocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/quickDoc/KotlinDocumentationTarget.kt)
PsiElementDocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/lang/documentation/psi/PsiElementDocumentationTarget.kt)
Deprecation NoticeAs of IntelliJ Platform version 2023.1, the Documentation Provider API is deprecated and plugin authors should use the instead.
Custom language developers usually extend from AbstractDocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/AbstractDocumentationProvider.java) instead of implementing the DocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/DocumentationProvider.java) interface. This implementation needs to be registered in com.intellij.lang.documentationProvider extension point.
The main work is done in generateDoc(), which has two PSI element arguments: the target element for which the documentation is requested and the original element under the cursor. If IntelliJ Platform's choice of the target element isn't suitable for your language, you can override getCustomDocumentationElement() and provide the correct element.
Once these steps are completed, the following additional features can be implemented:
Implement getQuickNavigateInfo() to provide the text that should be displayed when an element is hovered over with Ctrl/Cmd pressed.
Implement generateHoverDoc() to show different contents on mouse hover.
Implement getDocumentationElementForLookupItem() to return a suitable PSI element for the given lookup element when View | Quick Documentation is called on an element of the autocompletion popup.
Implement getUrlFor() and ExternalDocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/ExternalDocumentationProvider.java) to fetch documentation for elements from online resources.
The custom language tutorial (20. Documentation) contains a step-by-step guide for the DocumentationProvider of the Simple language. In addition, several implementations of other languages exist in the IntelliJ Platform code, for instance:
The Properties Language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties) has a small and easy-to-understand DocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/com/intellij/lang/properties/PropertiesDocumentationProvider.java) similar to the one shown in the custom language tutorial.
Usage examples for DocumentationMarkup can be found in ThemeJsonDocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/devkit/intellij.devkit.themes/src/ThemeJsonDocumentationProvider.java).
Additionally, custom actions can be incorporated into documentation inlays and popups using the DocumentationActionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationActionProvider.java). This provider should be registered with the com.intellij.documentationActionProvider extension point.
How the documentation for the target element is created is up to the custom language developer. A common choice is to extract and format documentation comments. To format the documentation contents, you should use DocumentationMarkup (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/DocumentationMarkup.java) to achieve a consistent output.
Use HtmlSyntaxInfoUtil (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/openapi/editor/richcopy/HtmlSyntaxInfoUtil.java) to create Lexer-based highlighted code samples.
Product Help: Parameter info (https://www.jetbrains.com/help/idea/viewing-reference-information.html#view-parameter-info)
Custom languages can use ParameterInfoHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java) registered in com.intellij.codeInsight.parameterInfo extension point (EP) to show information about parameters in method and function calls. This is a convenient way to display type signatures directly as a popup in the editor without having to consult the documentation. If it is available, the IDE can show this popup automatically after a short delay, or it can be invoked explicitly via View | Parameter Info.
Parameter info is dynamic and can update the displayed information when the caret is moved or additional code is typed. This allows for highlighting entries or marking the current parameter at the caret position. Therefore, the interface of the ParameterInfoHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java) EP consists of methods for initially collecting the required information to display parameter information at the caret position as well as methods to update what should be displayed during edits.
Language authors implement ParameterInfoHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandler.java) which takes two type parameters: ParameterOwner and ParameterType. For the explanations that follow, we assume that ParameterOwner is a PSI element that represents a function call in a language, and ParameterType represents (possibly several) function definitions.
Additionally, ParameterInfoHandler uses several context types that are mutable and used to adjust what and how parameter information is displayed. These contexts are, e.g., CreateParameterInfoContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/CreateParameterInfoContext.java), UpdateParameterInfoContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/UpdateParameterInfoContext.java) and ParameterInfoUIContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUIContext.java) and they all derive from ParameterInfoContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoContext.java).
The initial phase describes what happens when no parameter info is currently displayed and it is invoked either automatically or by the user.
The findElementForParameterInfo() method is called. When overriding this method, language authors use the provided CreateParameterInfoContext to access, e.g., the file and offset of the current editor. The goal is to identify the ParameterOwner i.e. a function call at the current offset if it exists. It is advised to extract the actual search for the function call into a separate method since it can be re-used later. The findElementForParameterInfo() implementation should find all matching function definitions and store them using setItemsToShow() of the context parameter.
If the returned function call element is valid, the showParameterInfo() method is invoked. Implementations of this method usually just call showHint() of the CreateParameterInfoContext providing the offset at which the popup should appear.
For each item to show from step 1, the updateUI() method is called. No heavy work is allowed in this method since it runs on EDT, and it should only update the UI representation using, e.g., setUIComponentEnabled() or setupUIComponentPresentation() of the provided ParameterInfoUIContext.
After that the following methods are called which will be explained in the next phase: findElementForUpdatingParameterInfo(), updateParameterInfo(), updateUI().
When a parameter info popup is displayed and the user types something or moves the caret, the displayed information is updated. This allows for, e.g., highlighting a function usage with different arguments or simply moving the parameter info box closer to the caret. Therefore, when the user moves the caret or types something, the following happens:
The syncUpdateOnCaretMove() method is called.
The findElementForUpdatingParameterInfo() method is called and it should find the correct function call (ParameterOwner) for the changed caret position. Implementations return null if an appropriate element could not be found or if it is different from getParameterOwner() of the provided UpdateParameterInfoContext. If null is returned, the dispose() method is called.
The processFoundElementForUpdatingParameterInfo() method is called which allows for additional adjustments of the UpdateParameterInfoContext. By default, this method does nothing and it's usually not necessary to implement it.
The updateParameterInfo() is called. Many implementations only invoke setParameterOwner() of the UpdateParameterInfoContext here.
The updateUI() method is called for each item in the getItemsToShow() array of the context which were collected in the initial phase.
Language authors can implement ParameterInfoHandlerWithTabActionSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoHandlerWithTabActionSupport.java) to extend the parameter info functionality with the ability to jump between parameter positions by pressing the tab key. For recurring tasks like finding the index of the current parameter in a function call, ParameterInfoUtils (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/lang/parameterInfo/ParameterInfoUtils.java) provides a collection of useful functions.
It is further helpful to inspect all the context-interfaces that extend from ParameterInfoContext and can be found in the com.intellij.lang.parameterInfo package as they provide insight into what data of the parameter info can be accessed and changed in the different stages.
Methods of the ParameterInfoHandler that have a default implementation can usually be ignored. syncUpdateOnCaretMove() and supportsOverloadSwitching() are used internally by the IntelliJ Platform and are not required to be implemented by plugins. The dispose() method is called when the currently displayed parameter info is invalidated and destroyed. Only isWhitespaceSensitive() which is used in the getCurrentOffset() method of ParameterInfoControllerBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoControllerBase.java) should be implemented when whitespace matters in a language.
Note that parameter info works during indexing (using incomplete data) when the implementations is marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs"). It is recommended to adapt tests for dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") since the results might be surprising, and more changes to the handler might be required for better results.
Finally, language authors should be aware of the global CodeInsightSettings#SHOW_FULL_SIGNATURES_IN_PARAMETER_INFO (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/codeInsight/CodeInsightSettings.java) setting that can be used to present results that are consistent with the default IDE behavior. For Java, for instance, the IDE shows the full signature of the method/function on parameter info if this setting is enabled.
Existing, moderately complex, implementations of ParameterInfoHandler in the IntelliJ Platform that can serve as a reference are:
XPathParameterInfoHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/XPathParameterInfoHandler.java)
XmlParameterInfoHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/codeInsight/hint/api/impls/XmlParameterInfoHandler.java)
Implementations of 3rd party plugins can be discovered using the IntelliJ Platform Explorer (https://plugins.jetbrains.com/intellij-platform-explorer?extensions=com.intellij.codeInsight.parameterInfo). Two examples are:
RsParameterInfoHandler (https://github.com/intellij-rust/intellij-rust/blob/93853e33261174399babb10e43a3aff133f0a5ef/src/main/kotlin/org/rust/ide/hints/parameter/RsParameterInfoHandler.kt) of the Rust plugin.
LatexParameterInfoHandler (https://github.com/Hannah-Sten/TeXiFy-IDEA/blob/655644b217e3954ae1286d7070a9357f2748f6a6/src/nl/hannahsten/texifyidea/documentation/LatexParameterInfoHandler.kt) of the TeXiFy plugin.
Product Help: Inlay Hints (https://www.jetbrains.com/help/idea/inlay-hints.html)
Inlay hints render small pieces of information directly into the editor and give developers additional code insight without disturbing the workflow. A well-known example is parameter hints that usually display the name of the function parameters as given in its declaration. They are closely related to Parameter Info which shows parameter types for all possible overloads of a function but opens as a popup overlaying the code.
Inlay hints are flexible and have a wide range of applications in the IntelliJ Platform. For instance, the following are well-known examples where inlay hints are used:
Java uses inlays to display type annotations in Java chained method calls.
Kotlin uses inlays in range expressions to show, e.g. less-than, or less-than-or-equal signs to let developers know if intervals are inclusive or exclusive.
In version-controlled projects, the author of the code is shown using inlay hints.
The main characteristic of the inlay is the way it is displayed in the editor:
inline - inlays displayed in the code between code tokens
block - inlays displayed above a code block
Depending on the requirements and target IntelliJ Platform version, there are several extension points to choose from, when implementing inlay hints.
This section describes the available APIs and their use cases.
To inspect existing Inlays in the IDE, use UI Inspector ("Editor" in "Internal Actions - UI Inspector"). Corresponding entries from Settings | Editor | Inlay Hints are also available from "Inspecting Settings" in "Internal Actions - UI Inspector".
Inlay parameter hints are simple string inline inlays placed before parameter names in method and function calls. It is not possible to provide advanced presentation and behavior of inlay parameter hints.
To provide inlay parameter hints, implement InlayParameterHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java) and register it in com.intellij.codeInsight.parameterNameHints extension point (EP). The API documentation of InlayParameterHintsProvider explains in detail the rationale behind all methods.
Examples:
GroovyInlayParameterHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/hint/GroovyInlayParameterHintsProvider.kt) - shows parameter hints in Groovy code
KotlinInlayParameterHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinInlayParameterHintsProvider.kt) - shows parameter hints in Kotlin code
To suppress inlay parameter hints in specific places, implement ParameterNameHintsSuppressor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/ParameterNameHintsSuppressor.kt) and register it in com.intellij.codeInsight.parameterNameHintsSuppressor EP.
This API is available since 2023.1.
Declarative inlay hints are inline textual inlays that can hold expandable list of clickable items. Please note this API has limited presentation customization possibilities due to its UI-independent design, which allows utilizing it by different frontend technologies (not only in Swing).
To provide declarative inlay hints implement declarative InlayHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/declarative/InlayHintsProvider.kt) and register it in com.intellij.codeInsight.declarativeInlayProvider EP. See the API documentation for the details.
Examples:
JavaImplicitTypeDeclarativeInlayHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/hints/JavaImplicitTypeDeclarativeInlayHintsProvider.kt) - shows inferred type for variables declared with the var keyword in Java code when the inferred type may not be clear
JavaMethodChainsDeclarativeInlayProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/hints/JavaMethodChainsDeclarativeInlayProvider.kt) - shows method return types in call chains in Java code
This API is available since 2022.1. It is still in experimental state and may be changed without preserving backward compatibility.
Code vision provider allows for providing block inlay hints for elements like class, method, field, etc. If there are multiple hints provided for a single element, all will be displayed in the same line to save vertical space.
Code vision hints can be displayed over the element, or on the right, at the end of line. It is configurable by users in Settings | Editor | Inlay Hints | Code vision by choosing a value in Default position for metrics combo box, or by selecting Position in specific provider entries.
There are two extension points for implementing a code vision provider:
DaemonBoundCodeVisionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/hints/codeVision/DaemonBoundCodeVisionProvider.kt) registered in com.intellij.codeInsight.daemonBoundCodeVisionProvider EP
CodeVisionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/codeVision/CodeVisionProvider.kt) registered in com.intellij.codeInsight.codeVisionProvider EP
DaemonBoundCodeVisionProvider API should be used in cases when code vision entries are related to PSI, so that calculated values are invalidated and recalculated on PSI changes.
CodeVisionProvider API should be used for cases when presented information doesn't depend on the PSI.
Examples:
JavaInheritorsCodeVisionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaInheritorsCodeVisionProvider.kt) - shows number of Java class or method inheritors. Clicking the inlay hint opens the list of inheritors. This provider is DaemonBoundCodeVisionProvider.
JavaReferencesCodeVisionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaReferencesCodeVisionProvider.kt) - shows number of usages of Java class or member. Clicking the inlay opens the list of usages or navigates to the usage if only one exists. This provider is DaemonBoundCodeVisionProvider.
VcsCodeVisionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-impl/src/com/intellij/codeInsight/hints/VcsCodeVisionProvider.kt) - shows the author of a given element, e.g., class or method, based on VCS information. This provider is CodeVisionProvider.
Inlay hints provider allows for implementing both inline and block inlay hints with custom presentation and behavior. See the API documentation for the details.
Deprecation NoticeFor implementing inline inlay hints in versions 2023.1 and newer, is recommended.
For implementing block inlay hints in versions 2022.1 and newer, is recommended.
To provide inlay hints, implement InlayHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsProvider.kt) and register it in com.intellij.codeInsight.inlayProvider EP. See the API documentation for the details.
Examples:
GroovyLocalVariableTypeHintsInlayProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInsight/hint/types/GroovyLocalVariableTypeHintsInlayProvider.kt) - shows local variable types in Groovy code
MarkdownTableInlayProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/src/org/intellij/plugins/markdown/editor/tables/ui/MarkdownTableInlayProvider.kt) - decorates tables in Markdown files.
For a more complex example, see KotlinLambdasHintsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/codeInsight/hints/KotlinLambdasHintsProvider.kt), its parent class and all implementations.
Go to Settings | Editor | Inlay Hints (Product Help (https://www.jetbrains.com/help/idea/inlay-hints.html)) and check out inlays that have already been implemented.
To support multiple languages with a single type of inlay hints, see declarative InlayHintsProviderFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/declarative/InlayHintsProviderFactory.kt) (2023.1+) or InlayHintsProviderFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hints/InlayHintsProviderFactory.kt) (pre-2023.1).
For testing inlay hints, see InlayHintsProviderTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayHintsProviderTestCase.kt) and InlayParameterHintsTest (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/utils/inlays/InlayParameterHintsTest.kt).
Product Help: Spell Checking (https://www.jetbrains.com/help/idea/spellchecking.html)
Spell Checking is used to check the correctness of natural languages within code. Language plugins can implement customized spell checking by implementing SpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java) and registering it in the com.intellij.spellchecker.support extension point.
Examples:
Custom Language Support Tutorial: Spell Checking (21. Spell Checking)
JavaSpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/spellchecker/JavaSpellcheckingStrategy.java)
HtmlSpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/spellchecker/xml/HtmlSpellcheckingStrategy.java)
SpellcheckingStrategy adjusts the spell checking behavior for PSI elements of a custom language by providing methods to define:
Which PSI elements should be checked by this strategy.
How to extract the text from PSI elements.
How the text is broken into single words.
The class already contains a default strategy for spell checking of basic parts such as comments, identifiers and plain text. If you don't need anything else, you can just inherit from this class and register it.
If you need to check spelling for some specific elements in your language, then override getTokenizer() and use isMyContext() to determine if a PSI element should be checked by your strategy. The getTokenizer() method returns an instance of Tokenizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/tokenizer/Tokenizer.java) and is explained below.
The tokenize() method of Tokenizer defines which portions of a PSI element need to be spell-checked by feeding them into the TokenConsumer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/tokenizer/TokenConsumer.java). In the simplest case, the whole PSI element is consumed and its entire text is split into words and checked for spelling. For these simple cases, SpellcheckingStrategy already contains predefined tokenizers:
SpellcheckingStrategy.TEXT_TOKENIZER for simple text elements.
SpellcheckingStrategy.EMPTY_TOKENIZER for elements that don't require checking.
myCommentTokenizer for comments.
myXmlAttributeTokenizer for XML attributes.
However, there are situations where only fragments of the PSI element are textual content. In these cases, tokenize() can take care of extracting the correct text-ranges and feed them sequentially into the TokenConsumer. If elements in your language require such special handling, then define a tokenizer by deriving from Tokenizer and implement tokenize() with the logic you need.
Example: MethodNameTokenizerJava (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/spellchecker/MethodNameTokenizerJava.java)
In Tokenizer.tokenize() the consumeToken() method can take an instance of Splitter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/inspections/Splitter.java) as the second argument. The Splitter defines how the text is broken into words which is not always as simple as splitting at white space. Consider, for instance, identifiers or variables that follow camel-case or snake-case naming and that need to be separated differently to spell check single parts. As an example, please see how IdentifierSplitter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/inspections/IdentifierSplitter.java), splits identifiers into separate words.
A custom language can define special splitting rules for elements by deriving from Splitter and implementing the logic for obtaining words from the passed text in the split() method.
Custom languages that support the suppression of inspection annotations can derive from SuppressibleSpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/tokenizer/SuppressibleSpellcheckingStrategy.java) to make spell checking suppressible. The implementation overrides isSuppressedFor() to check if a spell check warning is suppressed for the passed element and overriding getSuppressActions() to add quick fix actions that suppress warnings.
Example: XmlSpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/spellchecker/xml/XmlSpellcheckingStrategy.java)
Some custom languages may have a distinct fixed set of words or key identifiers. These words can be provided in additional dictionaries from BundledDictionaryProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/BundledDictionaryProvider.java). Implement getBundledDictionaries() to return paths to the word dictionaries (*.dic files) and register it with the com.intellij.spellchecker.bundledDictionaryProvider extension point.
Example: PythonBundledDictionaryProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/spellchecker/PythonBundledDictionaryProvider.java)
RuntimeDictionaryProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/dictionary/RuntimeDictionaryProvider.java) allows providing (dynamic) dictionaries generated at runtime, e.g., downloaded from a server, created from project sources on-the-fly, etc. Register in com.intellij.spellchecker.dictionary.runtimeDictionaryProvider extension point.
Example PyPackagesDictionary (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/packaging/PyPackagesDictionary.kt)
A number of commonly used minor features are listed in the following format:
EP: fully.qualified.extensionPointName — Extension Point Name (must be specified in plugin.xml (Plugin Configuration File))
com.extensionPoint.class description text — Extension Point class/interface to provide functionality
- Sample 1 - Sample implementation
Locating more Language EPsSee also "LangExtensionPoints.xml" in "IntelliJ Platform Extension Point and Listener List" to discover more Language-related Extension Points as well as general guide Explore the IntelliJ Platform API.
EP: com.intellij.lang.braceMatcher
PairedBraceMatcher (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/PairedBraceMatcher.java) returns an array of brace pairs (BracePair (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/BracePair.java)) specifying the characters for the opening and closing braces and the lexer token types for these characters. (In principle, it is possible to return multi-character tokens, like "begin" and "end", as the start and end tokens of a brace pair. The IDE will match such braces, but the highlighting for such braces will not be entirely correct.)
Certain types of braces can be marked as structural. Structural braces have higher priority than regular braces: they are matched with each other even if there are unmatched braces of different types between them. An opening non-structural brace is not matched with a closing one if one of them is inside a pair of matched structural braces and another is outside. See also .
If the brace matching is "too heavy" and should not be executed in EDT, implement HeavyBraceHighlighter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HeavyBraceHighlighter.java) and register in com.intellij.heavyBracesHighlighter EP (2022.3).
EP: com.intellij.lang.quoteHandler
To support Insert pair quote feature, provide QuoteHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/QuoteHandler.java). In most cases, SimpleTokenSetQuoteHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SimpleTokenSetQuoteHandler.java) base implementation will be suitable.
EP: com.intellij.lang.commenter
Commenter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Commenter.java) returns the prefix for the line comment, and the prefix and suffix for the block comment if supported by the language. For more complex logic, use SelfManagingCommenter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/codeInsight/generation/SelfManagingCommenter.java).
Commenter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/properties-psi-impl/src/com/intellij/lang/properties/PropertiesCommenter.java) for Properties language plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties)
Custom Language Support Tutorial: Commenter (18. Commenter)
EP: com.intellij.lang.foldingBuilder
FoldingBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/folding/FoldingBuilder.java) returns the list of foldable text ranges (as an array of FoldingDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/folding/FoldingDescriptor.java) objects), the replacement text which is shown for each range when it is folded, and the default state of each folding region (folded or unfolded).
Custom Language Support Tutorial: Folding Builder (12. Folding Builder)
EP: com.intellij.joinLinesHandler
JoinLinesHandlerDelegate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/JoinLinesHandlerDelegate.java) allows extending support smart/semantic Edit | Join Lines (e.g., String literal split on multiple lines).
EP: com.intellij.lang.smartEnterProcessor
SmartEnterProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/smartEnter/SmartEnterProcessor.java) handles Edit | Complete Statement (e.g., autocomplete missing semicolon/parentheses).
EP: com.intellij.moveLeftRightHandler
Return children of given element from MoveElementLeftRightHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/moveLeftRight/MoveElementLeftRightHandler.java) for Code | Move Element Left|Right, e.g., method call parameters. Alternatively, implement PsiListLikeElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiListLikeElement.java) in PSI element.
EP: com.intellij.nameSuggestionProvider
NameSuggestionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/refactoring/rename/NameSuggestionProvider.java) provides name suggestions for the given element, e.g., for Rename refactoring.
EP: com.intellij.highlightUsagesHandlerFactory
HighlightUsagesHandlerFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/highlighting/HighlightUsagesHandlerFactory.java) allows highlighting e.g., Exit Points or Exceptions.
ParserDefinition.getCommentTokens() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java) must return the set of tokens treated as comments to populate the TODO tool window.
EP: com.intellij.indexPatternSearch
Additional places can be provided via IndexPatternSearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/searches/IndexPatternSearch.java) registered in com.intellij.indexPatternSearch extension point.
EP: com.intellij.declarationRangeHandler
DeclarationRangeHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/hint/DeclarationRangeHandler.java) provides View | Context Info for custom languages with structure view implementation based on a TreeBasedStructureViewBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/TreeBasedStructureViewBuilder.java).
Moved to Spell Checking.
EP: com.intellij.referenceInjector
ReferenceInjector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/psi/injection/ReferenceInjector.java) allows users to inject pre-defined references (e.g., "Encoding", "File Reference") into PsiLanguageInjectionHost elements (IntelliLang plugin required).
EP: com.intellij.colorProvider
ElementColorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/openapi/editor/ElementColorProvider.java) renders gutter icon for an element containing color information.
EP: com.intellij.include.provider
FileIncludeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeProvider.java) provides information about include statements resolving to files (e.g., <xi:include> in XML). Including/included files can then be obtained via FileIncludeManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeManager.java).
EP: com.intellij.codeBlockSupportHandler
CodeBlockSupportHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/highlighting/CodeBlockSupportHandler.java) allows providing text ranges for more complex code blocks like, e.g., in if/elsif/else blocks. It is used to highlight markers and keywords if one is under the cursor, and for navigation to the beginning/end of blocks. See also .
EP: com.intellij.breadcrumbsInfoProvider
BreadcrumbsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ui/breadcrumbs/BreadcrumbsProvider.java) allows for language-specific breadcrumbs.
EP: com.intelllij.completion.plainTextSymbol
PlainTextSymbolCompletionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/completion/PlainTextSymbolCompletionContributor.java) provides a simple way to extract lookup elements from a file so that users have completion available in, e.g., plain text editors like VCS commit messages.
EP: com.intellij.listSplitJoinContext
ListSplitJoinContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/openapi/editor/actions/lists/ListSplitJoinContext.kt) needs to be implemented to define the exact behavior of splitting and joining list-like constructs in a language. The UI will show implementations of this EP as an intention action (https://www.jetbrains.com/help/idea/intention-actions.html) at appropriate locations. Developers can use the abstract classes in DefaultListSplitJoinContext (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/openapi/editor/actions/lists/DefaultListSplitJoinContext.kt) for their implementation.
EP: com.intellij.suggestedRefactoringSupport
SuggestedRefactoringSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/refactoring/suggested/SuggestedRefactoringSupport.kt) provides functionality for suggesting rename and change signature refactorings for custom languages.
EP: com.intellij.readerModeMatcher
ReaderModeMatcher (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/codeInsight/actions/ReaderModeMatcher.kt) provides a way to decide if files are shown in the correct mode: reader mode vs. normal editor mode. Please see the documentation (https://www.jetbrains.com/help/idea/reader-mode.html) to get familiar with reader mode.
EP: com.intellij.editorTabColorProvider
EditorTabColorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java) allows for the modification of the background colors for specific files. If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
EP: com.intellij.editorTabTitleProvider
EditorTabTitleProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabTitleProvider.java) allows for specifying custom names and tooltips displayed in the title of editor tabs. If access to indexes is not required, it can be marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs").
Please see, e.g., GradleEditorTabTitleProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/src/org/jetbrains/plugins/gradle/util/GradleEditorTabTitleProvider.kt) which shows how the project name is added to the editor tab for Gradle files.
EP: com.intellij.problemHighlightFilter, com.intellij.problemFileHighlightFilter
ProblemHighlightFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/daemon/ProblemHighlightFilter.java) and the com.intellij.problemFileHighlightFilter EP (which implements Condition<VirtualFile> (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/Condition.java)) are used to filter out files that should not be error-highlighted because they are, e.g., outside the current project scope. Note that these filters should be permissive and only prevent highlighting for files that are absolutely known to be outside the scope.
JavaProblemHighlightFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/daemon/JavaProblemHighlightFilter.java)
PyProblemFileHighlightFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/com/jetbrains/python/codeInsight/PyProblemFileHighlightFilter.java)
EP: com.intellij.ide.actions.QualifiedNameProvider
QualifiedNameProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/ide/actions/QualifiedNameProvider.java) provides features like copying and pasting references of FQN for, e.g., classes, functions, or methods. Therefore, the QualifiedNameProvider implementation needs to provide logic to convert from and to FQN.
EP: com.intellij.openapi.roots.TestSourcesFilter
TestSourcesFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/src/com/intellij/openapi/roots/TestSourcesFilter.java) allows for telling the IDE that a file is a test file, even it's not located in a directory marked as test root. This can be used in situations where test files are located next to source files. If these files can be distinguished either by filename or content from source files, implementing this EP will mark them as test files for the IDE.
EP: com.intellij.statementUpDownMover
StatementUpDownMover (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/editorActions/moveUpDown/StatementUpDownMover.java) allows for customizing the behavior of moving statements up and down. This can be used to keep code syntactically correct when moving code in the editor, e.g. when moving a variable declaration.
DeclarationMover (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/codeInsight/editorActions/moveUpDown/DeclarationMover.java)
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
In this tutorial, we will add support for a .properties (https://en.wikipedia.org/wiki/.properties) language and its usages within Java code.
Navigating this tutorialIntelliJ Platform support for custom languages is discussed in more depth in the Custom Language Support (Custom Language Support) section. Corresponding parts are linked under Reference on top of each page in this tutorial.
All relevant code added or changed on a page is linked in Code.
The accompanying Testing a Custom Language Plugin tutorial covers testing the functionality; corresponding parts are linked under Testing.
Accessing the codeThe complete and fully working example plugin used in this tutorial is the simple_language_plugin (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin) code sample.
See Code Samples on how to build and run it.
This a step-by-step tutorial, and it requires completing each step, in order:
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
This page is part of multi step Custom Language Support Tutorial.
Download and install either IntelliJ IDEA Ultimate or IntelliJ IDEA Community Edition from here (https://www.jetbrains.com/idea/download/).
While not required, having the full sources of the platform and all bundled plugins available for browsing allows finding related implementations.
Download the IntelliJ IDEA Community Edition source files as described in the IntelliJ IDEA Community Edition README (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/README.md) file.
Make sure that the Plugin DevKit and Gradle plugins are enabled.
Plugin DevKit plugin is bundled with IntelliJ IDEA until 2023.2.
Plugin DevKit AvailabilityWhen using IntelliJ IDEA 2023.3 or later, the Plugin DevKit plugin must be installed from JetBrains Marketplace (Plugin Homepage (https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is no longer bundled with the IDE.
Install and enable Grammar-Kit (https://plugins.jetbrains.com/plugin/6606-grammar-kit) and PsiViewer (https://plugins.jetbrains.com/plugin/227-psiviewer) plugins.
Create an empty IntelliJ Platform Plugin project (Creating a Plugin Gradle Project) or start using IntelliJ Platform Plugin Template when creating a plugin hosted on GitHub.
Reference: Registering a File Type
Code: SimpleLanguage (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLanguage.java), SimpleIcons (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleIcons.java), SimpleFileType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFileType.java)
This page is part of multi step Custom Language Support Tutorial.
The IntelliJ Platform determines file type by examining the name of a file. Each language has Language (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Language.java) and LanguageFileType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java) objects defining the language. Register the LanguageFileType with the IntelliJ Platform in the plugin configuration file.
The language implemented in this tutorial is named "Simple" - note the case of the name. The SimpleLanguage (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLanguage.java) class is defined in the org.intellij.sdk.language package of the simple_language_plugin code sample:
The icon (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/resources/icons/jar-gray.png) for the Simple Language is defined by the SimpleIcons (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleIcons.java) class. Please see Working with Icons for details on how to define and use icons.
The SimpleFileType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFileType.java) is defined by subclassing LanguageFileType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/LanguageFileType.java):
The Simple Language file type is registered via the com.intellij.fileType extension point in plugin.xml (Plugin Configuration File) and registered with *.simple extension:
<extensions defaultExtensionNs="com.intellij">
<fileType
name="Simple File"
implementationClass="org.intellij.sdk.language.SimpleFileType"
fieldName="INSTANCE"
language="Simple"
extensions="simple"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Create an empty file with the extension .simple, and IntelliJ IDEA automatically associates it with our language. Note the appearance of the Simple Language file icon next to the test.simple file in the Project Tool Window, and the editor tab for the file.

Reference: Implementing Lexer, Implementing Parser and PSI
Code: SimpleTokenType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleTokenType.java), SimpleElementType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleElementType.java)
This page is part of multi step Custom Language Support Tutorial.
In order for the IntelliJ Platform to parse a Simple Language file, tokens and elements must be defined based on IElementType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/tree/IElementType.java). The Simple Language grammar must also be defined to generate a parser.
Create SimpleTokenType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleTokenType.java) in the org.intellij.sdk.language.psi package by subclassing IElementType.
Create the SimpleElementType (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleElementType.java) in the org.intellij.sdk.language.psi package by subclassing IElementType.
Define a grammar for the Simple Language in the org/intellij/sdk/language/Simple.bnf file.
{
parserClass="org.intellij.sdk.language.parser.SimpleParser"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="Simple"
psiImplClassSuffix="Impl"
psiPackage="org.intellij.sdk.language.psi"
psiImplPackage="org.intellij.sdk.language.psi.impl"
elementTypeHolderClass="org.intellij.sdk.language.psi.SimpleTypes"
elementTypeClass="org.intellij.sdk.language.psi.SimpleElementType"
tokenTypeClass="org.intellij.sdk.language.psi.SimpleTokenType"
}
simpleFile ::= item_*
private item_ ::= (property|COMMENT|CRLF)
property ::= (KEY? SEPARATOR VALUE?) | KEYPlease see Grammar-Kit (https://github.com/JetBrains/Grammar-Kit) documentation for more details on BNF syntax.
The grammar defines the flexibility of the support for a language. The above grammar specifies that a property may have or may not have a key and value. This flexibility allows the IntelliJ Platform to recognize incorrectly defined properties and provide corresponding code analysis and quick fixes.
Note that the SimpleTypes class in the elementTypeHolderClass attribute above specifies the name of a class that gets generated from the grammar in the scope of the Generate Parser Code action (see ); it doesn't exist at this point.
Now that the grammar is defined, generate a parser with PSI classes via Generate Parser Code from the context menu on the Simple.bnf file. This step generates a parser and PSI elements in the /src/main/gen folder of the project.
Gradle Grammar-Kit Plugin can be used alternatively.

To include the sources generated into /src/main/gen, the project's sourceSets must be expanded by inserting the following line in the project's Gradle build script:
sourceSets["main"].java.srcDirs("src/main/gen")sourceSets.main.java.srcDirs 'src/main/gen'See build.gradle.kts (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/build.gradle.kts) for the reference.
Reload the Gradle project for changes to take effect and build the project.
Reference: Implementing Lexer
Code: Simple.flex (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/Simple.flex), SimpleLexerAdapter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLexerAdapter.java), SimpleFile (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleFile.java), SimpleTokenSets (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleTokenSets.java), SimpleParserDefinition (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleParserDefinition.java)
Testing: 2. Parsing Test
This page is part of multi step Custom Language Support Tutorial.
The lexical analyzer defines how the contents of a file are broken into tokens, which is the basis for supporting custom language features. The easiest way to create a lexer is to use JFlex (https://jflex.de/).
Define a Simple.flex (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/Simple.flex) file with rules for the Simple Language lexer in package org.intellij.sdk.language.
// Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.intellij.sdk.language;
import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import org.intellij.sdk.language.psi.SimpleTypes;
import com.intellij.psi.TokenType;
%%
%class SimpleLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
%eof{ return;
%eof}
CRLF=\R
WHITE_SPACE=[\ \n\t\f]
FIRST_VALUE_CHARACTER=[^ \n\f\\] | "\\"{CRLF} | "\\".
VALUE_CHARACTER=[^\n\f\\] | "\\"{CRLF} | "\\".
END_OF_LINE_COMMENT=("#"|"!")[^\r\n]*
SEPARATOR=[:=]
KEY_CHARACTER=[^:=\ \n\t\f\\] | "\\ "
%state WAITING_VALUE
%%
<YYINITIAL> {END_OF_LINE_COMMENT} { yybegin(YYINITIAL); return SimpleTypes.COMMENT; }
<YYINITIAL> {KEY_CHARACTER}+ { yybegin(YYINITIAL); return SimpleTypes.KEY; }
<YYINITIAL> {SEPARATOR} { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; }
<WAITING_VALUE> {CRLF}({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {WHITE_SPACE}+ { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; }
<WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}* { yybegin(YYINITIAL); return SimpleTypes.VALUE; }
({CRLF}|{WHITE_SPACE})+ { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }
[^] { return TokenType.BAD_CHARACTER; }Now generate a lexer class via Run JFlex Generator from the context menu on Simple.flex file.
Users from China, please see important configuration (https://github.com/JetBrains/Grammar-Kit/issues/300#issuecomment-1476498645).
The Grammar-Kit plugin uses the JFlex lexer generation. When running for the first time, JFlex prompts for a destination folder to download the JFlex library and skeleton. Choose the project root directory, for example code_samples/simple_language_plugin.
After that, the IDE generates the lexer under the gen directory, for example in simple_language_plugin/src/main/gen/org/intellij/sdk/language/SimpleLexer.
Gradle Grammar-Kit Plugin can be used alternatively.
The JFlex lexer needs to be adapted to the IntelliJ Platform Lexer API. Implement SimpleLexerAdapter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLexerAdapter.java) by subclassing FlexAdapter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lexer/FlexAdapter.java).
The SimpleFile (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleFile.java) implementation is the top-level node of the tree of PsiElements (Implementing Parser and PSI) for a Simple Language file.
Define all sets of related token types from SimpleTypes in SimpleTokenSets (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleTokenSets.java).
The Simple Language parser is defined in SimpleParserDefinition (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleParserDefinition.java) by subclassing ParserDefinition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/ParserDefinition.java). To avoid unnecessary classloading when initializing the extension point implementation, all TokenSet return values should use constants from dedicated $Language$TokenSets class.
Registering the parser definition in the plugin.xml (Plugin Configuration File) file makes it available to the IntelliJ Platform. Use the com.intellij.lang.parserDefinition extension point for registration. For example, see simple_language_plugin/src/main/resources/META-INF/plugin.xml.
<extensions defaultExtensionNs="com.intellij">
<lang.parserDefinition
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleParserDefinition"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Create a test.simple file with the following content:
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009Now open the PsiViewer tool window and check how the lexer breaks the content of the file into tokens, and the parser transforms the tokens into PSI elements.

Reference: Syntax and Error Highlighting
Code: SimpleSyntaxHighlighter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSyntaxHighlighter.java), SimpleSyntaxHighlighterFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSyntaxHighlighterFactory.java), SimpleColorSettingsPage (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleColorSettingsPage.java)
This page is part of multi step Custom Language Support Tutorial.
The first level of syntax highlighting is based on the lexer output, and is provided by SyntaxHighlighter. A plugin can also define color settings based on ColorSettingPage so the user can configure highlight colors.
The SimpleSyntaxHighlighter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSyntaxHighlighter.java) class extends SyntaxHighlighterBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighterBase.java). As recommended in Color Scheme Management ("Text Attribute Key Dependency" in "Color Scheme Management"), the Simple Language highlighting text attributes are specified as a dependency on one of standard IntelliJ Platform keys. For the Simple Language, define only one scheme.
The factory provides a standard way for the IntelliJ Platform to instantiate the syntax highlighter for Simple Language files. Here, SimpleSyntaxHighlighterFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSyntaxHighlighterFactory.java) subclasses SyntaxHighlighterFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighterFactory.java).
Register the factory with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.syntaxHighlighterFactory extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.syntaxHighlighterFactory
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleSyntaxHighlighterFactory"/>
</extensions>Open the example Simple Language properties file ("Run the Project" in "4. Lexer and Parser Definition") (test.simple) in the IDE Development Instance. The colors for Simple Language Key, Separator, and Value highlighting default to the IDE Language Defaults for Keyword, Braces, Operators, and Strings, respectively.

The color settings page adds the ability for users to customize color settings for the highlighting in Simple Language files. The SimpleColorSettingsPage (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleColorSettingsPage.java) implements ColorSettingsPage (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/options/colors/ColorSettingsPage.java).
It is supported to group related attributes like operators or braces by separating the nodes with //, e.g.:
AttributesDescriptor[] DESCRIPTORS = new AttributesDescriptor[] {
new AttributesDescriptor("Operators//Plus", MySyntaxHighlighter.PLUS),
new AttributesDescriptor("Operators//Minus", MySyntaxHighlighter.MINUS),
new AttributesDescriptor("Operators//Advanced//Sigma", MySyntaxHighlighter.SIGMA),
new AttributesDescriptor("Operators//Advanced//Pi", MySyntaxHighlighter.PI),
//...
};Register the Simple Language color settings page with the IntelliJ Platform in the plugin configuration file using the com.intellij.colorSettingsPage extension point.
<extensions defaultExtensionNs="com.intellij">
<colorSettingsPage
implementation="org.intellij.sdk.language.SimpleColorSettingsPage"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
In the IDE Development Instance, open the Simple Language highlight settings page: Settings | Editor | Color Scheme | Simple. Each color initially inherits from a Language Defaults value.

Code: SimplePsiImplUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/impl/SimplePsiImplUtil.java), SimpleUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleUtil.java)
This page is part of multi step Custom Language Support Tutorial.
Helper classes and utilities can be embedded in the code generated by Grammar-Kit.
Custom methods in PSI (Program Structure Interface (PSI)) classes are defined separately, and Grammar-Kit embeds them into generated code. Define a utility class with these helper methods:
package org.intellij.sdk.language.psi.impl;
import com.intellij.lang.ASTNode;
import org.intellij.sdk.language.psi.SimpleProperty;
import org.intellij.sdk.language.psi.SimpleTypes;
public class SimplePsiImplUtil {
public static String getKey(SimpleProperty element) {
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
if (keyNode != null) {
// IMPORTANT: Convert embedded escaped spaces to simple spaces
return keyNode.getText().replaceAll("\\\\ ", " ");
} else {
return null;
}
}
public static String getValue(SimpleProperty element) {
ASTNode valueNode = element.getNode().findChildByType(SimpleTypes.VALUE);
if (valueNode != null) {
return valueNode.getText();
} else {
return null;
}
}
}The parser generates the SimpleProperty interface referenced in the code above.
Now the utility class is added to the grammar file via the psiImplUtilClass global attribute. Add methods attribute for a particular rule to specify which one should be used for PSI classes.
Compare the property rule below to the previous definition ("Define the Grammar" in "3. Grammar and Parser").
{
parserClass="org.intellij.sdk.language.parser.SimpleParser"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="Simple"
psiImplClassSuffix="Impl"
psiPackage="org.intellij.sdk.language.psi"
psiImplPackage="org.intellij.sdk.language.psi.impl"
elementTypeHolderClass="org.intellij.sdk.language.psi.SimpleTypes"
elementTypeClass="org.intellij.sdk.language.psi.SimpleElementType"
tokenTypeClass="org.intellij.sdk.language.psi.SimpleTokenType"
psiImplUtilClass="org.intellij.sdk.language.psi.impl.SimplePsiImplUtil"
}
simpleFile ::= item_*
private item_ ::= (property|COMMENT|CRLF)
property ::= (KEY? SEPARATOR VALUE?) | KEY
{
methods=[getKey getValue]
}After making above changes to the grammar, regenerate ("Generate a Parser" in "3. Grammar and Parser") the parser and PSI classes.
Create SimpleUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleUtil.java) utility class to search PSI elements for defined properties over the project. It will be used later when implementing code completion (9. Completion Contributor).
Reference: "Annotator" in "Syntax and Error Highlighting"
Code: SimpleAnnotator (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleAnnotator.java)
Testing: 4. Annotator Test
This page is part of multi step Custom Language Support Tutorial.
An Annotator helps highlight and annotate any code based on specific rules. This section adds annotation functionality to support the Simple Language in the context of Java code.
Classes defined in this step of the tutorial depend on com.intellij.psi.PsiLiteralExpression (the PSI representation for String literals in Java code) at runtime. Using PsiLiteralExpression introduces a dependency ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") on com.intellij.java.
Beginning in version 2019.2, a dependency on Java plugin must be declared explicitly ("Java" in "Plugin Compatibility with IntelliJ Platform Products"). First, add a dependency on the Java plugin in the Gradle build script:
intellij {
plugins.set(listOf("com.intellij.java"))
}intellij {
plugins = ['com.intellij.java']
}Then, declare the dependency in plugin.xml (Plugin Configuration File) (use code insight)
<depends>com.intellij.java</depends>The SimpleAnnotator (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleAnnotator.java) subclasses Annotator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/Annotator.java). Consider a literal string that starts with "simple:" as a prefix of a Simple Language key. It isn't part of the Simple Language, but it is a useful convention for detecting Simple Language keys embedded as string literals in other languages, like Java. Annotate the simple:key literal expression, and differentiate between a well-formed vs. an unresolved property.
The use of new AnnotationHolder syntax starting 2020.2, which uses the builder format.
If the above code is copied at this stage of the tutorial, then remove the line below the comment "** Tutorial step 19. …" The quick fix class in that line is not defined until later in the tutorial.
Using the com.intellij.annotator extension point in the plugin configuration file, register the Simple Language annotator class for JAVA language:
<extensions defaultExtensionNs="com.intellij">
<annotator
language="JAVA"
implementationClass="org.intellij.sdk.language.SimpleAnnotator"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
As a test, define the following Java file containing a Simple Language prefix:value pair:
public class Test {
public static void main(String[] args) {
System.out.println("simple:website");
}
}Open this Java file in an IDE Development Instance running the simple_language_plugin to check if the IDE resolves a property:

If the property is an undefined name, the annotator flags the code with an error.

Try changing the Simple Language color settings ("Run the Project" in "5. Syntax Highlighter and Color Settings Page") to differentiate the annotation from the default language color settings.
Code: SimpleLineMarkerProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLineMarkerProvider.java)
This page is part of multi step Custom Language Support Tutorial.
Line markers help annotate code with icons on the gutter. These markers can provide navigation targets to related code.
A line marker provider annotates usages of Simple Language properties within Java code and provides navigation to the definition of these properties. The visual marker is a Simple Language icon in the gutter of the Editor window.
The SimpleLineMarkerProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLineMarkerProvider.java) subclasses RelatedItemLineMarkerProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/daemon/RelatedItemLineMarkerProvider.java). For this example, override the collectNavigationMarkers() method to collect usage of a Simple Language key and separators ("Define the Language" in "2. Language and File Type"):
Extending from GutterIconDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterIconDescriptor.java) allows configuring gutter icons to be shown via Settings | Editor | General | Gutter Icons.
This section addresses important details about implementing a marker provider.
The collectNavigationMarkers() method should:
Only return line marker information consistent with the element passed into the method. For example, do not return a class marker if getLineMarkerInfo() was called with an element that corresponds to a method.
Return line marker information for the appropriate element at the correct scope of the PSI tree. Return leaf elements only - i.e., the smallest possible elements. For example, do not return method marker for PsiMethod (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiMethod.java). Instead, return it for the PsiIdentifier (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-api/src/com/intellij/psi/PsiIdentifier.java) which contains the name of the method.

What happens when a LineMarkerProvider returns marker information for a PsiElement that is a higher node in the PSI tree? For example, if MyWrongLineMarkerProvider() erroneously returns a PsiMethod instead of a PsiIdentifier element:
final class MyWrongLineMarkerProvider implements LineMarkerProvider {
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
if (element instanceof PsiMethod) {
return new LineMarkerInfo(element, ...);
}
return null;
}
}The consequences of the MyWrongLineMarkerProvider() implementation have to do with how the IntelliJ Platform performs inspections. For performance reasons, inspection, and specifically the LineMarkersPass (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/LineMarkersPass.java) queries all LineMarkerProviders (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/codeInsight/daemon/LineMarkerProviders.java) in two phases:
The first pass is for all elements visible in the Editor window,
The second pass is for the rest of the elements in the file.
If providers return nothing for either area, the line markers get cleared. However, if a method like actionPerformed() is not completely visible in the Editor window (as shown in the image above,) and MyWrongLineMarkerProvider() returns marker info for the PsiMethod instead of PsiIdentifier, then:
The first pass removes line marker info because whole PsiMethod isn't visible.
The second pass tries to add a line marker because MyWrongLineMarkerProvider() is called for the PsiMethod.
As a result, the line marker icon would blink annoyingly. To fix this problem for this case, rewrite MyWrongLineMarkerProvider to return info for PsiIdentifier instead of PsiMethod as shown below:
final class MyCorrectLineMarkerProvider implements LineMarkerProvider {
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
if (element instanceof PsiIdentifier &&
element.getParent() instanceof PsiMethod) {
return new LineMarkerInfo(element, ...);
}
return null;
}
}The SimpleLineMarkerProvider implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.codeInsight.lineMarkerProvider extension point.
<extensions defaultExtensionNs="com.intellij">
<codeInsight.lineMarkerProvider
language="JAVA"
implementationClass="org.intellij.sdk.language.SimpleLineMarkerProvider"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the Java Test file ("Run the Project" in "7. Annotator"). Now the icon appears next to line 3 on the gutter. A user can click on the icon to navigate to the property definition.

Reference: Code Completion
Code: SimpleCompletionContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCompletionContributor.java)
This page is part of multi step Custom Language Support Tutorial.
Custom languages provide code completion using one of two approaches: Contributor and Reference-based (see 10. Reference Contributor) completion.
For this tutorial, the simple_language_plugin provides custom completion for values in Simple Language property files. Create SimpleCompletionContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCompletionContributor.java) by subclassing CompletionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/completion/CompletionContributor.java). This rudimentary completion contributor always adds "Hello" to the completion variants result set, regardless of context:
The SimpleCompletionContributor implementation is registered in the plugin configuration file using the com.intellij.completion.contributor extension point and specifying language="Simple".
<extensions defaultExtensionNs="com.intellij">
<completion.contributor
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleCompletionContributor"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the test.simple ("Run the Project" in "4. Lexer and Parser Definition") file. Erase the property "English" and invoke Basic Code Completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#invoke-basic-completion). The choice "Hello" is shown:

Reference: References and Resolve, PSI References
Code: SimpleNamedElement (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleNamedElement.java), SimpleNamedElementImpl (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/impl/SimpleNamedElementImpl.java), SimpleReference.java (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleReference.java), SimpleReferenceContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleReferenceContributor.java), SimpleRefactoringSupportProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleRefactoringSupportProvider.java)
Testing: 3. Completion Test, 6. Rename Test, 10. Reference Test,
This page is part of multi step Custom Language Support Tutorial.
The references functionality is one of the most important parts in the implementation of custom language support. Resolving references means the ability to go from the usage of an element to its declaration, completion, rename refactoring, find usages, etc.
Every PSI element that can be renamed or referenced needs to implement PsiNamedElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNamedElement.java) interface.
The classes below show how the Simple Language fulfills the need to implement PsiNamedElement.
The SimpleNamedElement (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleNamedElement.java) interface is subclassed from PsiNameIdentifierOwner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiNameIdentifierOwner.java).
The SimpleNamedElementImpl (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/impl/SimpleNamedElementImpl.java) class implements the SimpleNamedElement interface and extends ASTWrapperPsiElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/extapi/psi/ASTWrapperPsiElement.java).
Modify SimplePsiImplUtil to support new methods that get added to the PSI class for Simple Language. Note that SimpleElementFactory isn't defined until the next step, so for now it shows as an error.
public class SimplePsiImplUtil {
// ...
public static String getName(SimpleProperty element) {
return getKey(element);
}
public static PsiElement setName(SimpleProperty element, String newName) {
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
if (keyNode != null) {
SimpleProperty property =
SimpleElementFactory.createProperty(element.getProject(), newName);
ASTNode newKeyNode = property.getFirstChild().getNode();
element.getNode().replaceChild(keyNode, newKeyNode);
}
return element;
}
public static PsiElement getNameIdentifier(SimpleProperty element) {
ASTNode keyNode = element.getNode().findChildByType(SimpleTypes.KEY);
return keyNode != null ? keyNode.getPsi() : null;
}
// ...
}The SimpleElementFactory provides methods for creating SimpleFile.
package org.intellij.sdk.language.psi;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import org.intellij.sdk.language.SimpleFileType;
public class SimpleElementFactory {
public static SimpleProperty createProperty(Project project, String name) {
SimpleFile file = createFile(project, name);
return (SimpleProperty) file.getFirstChild();
}
public static SimpleFile createFile(Project project, String text) {
String name = "dummy.simple";
return (SimpleFile) PsiFileFactory.getInstance(project).
createFileFromText(name, SimpleFileType.INSTANCE, text);
}
}Now make corresponding changes to the Simple.bnf grammar file by replacing the property definition with the lines below. Don't forget to regenerate the parser after updating the file! Right-click on the Simple.bnf file and select Generate Parser Code.
property ::= (KEY? SEPARATOR VALUE?) | KEY {
mixin="org.intellij.sdk.language.psi.impl.SimpleNamedElementImpl"
implements="org.intellij.sdk.language.psi.SimpleNamedElement"
methods=[getKey getValue getName setName getNameIdentifier]
}Now define a reference class SimpleReference.java (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleReference.java) to resolve a property from its usage. This requires extending PsiReferenceBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReferenceBase.java) and implementing PsiPolyVariantReference (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiPolyVariantReference.java). The latter enables the reference to resolve to more than one element or to resolve result(s) for a superset of valid resolve cases.
A reference contributor allows the simple_language_plugin to provide references to Simple Language from elements in other languages such as Java. Create SimpleReferenceContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleReferenceContributor.java) by subclassing PsiReferenceContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java). Contribute a reference to each usage of a property:
The SimpleReferenceContributor implementation is registered using the com.intellij.psi.referenceContributor extension point and specifying language="JAVA".
<extensions defaultExtensionNs="com.intellij">
<psi.referenceContributor language="JAVA"
implementation="org.intellij.sdk.language.SimpleReferenceContributor"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
The IDE now resolves the property and provides completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#basic_completion) suggestions:

The Rename refactoring (https://www.jetbrains.com/help/idea/rename-refactorings.html#invoke-rename-refactoring) functionality is now available from definition and usages.

Support for in-place refactoring is specified explicitly in a refactoring support provider. Create SimpleRefactoringSupportProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleRefactoringSupportProvider.java) by subclassing RefactoringSupportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java) As long as an element is a SimpleProperty it is allowed to be refactored:
The SimpleRefactoringSupportProvider implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.refactoringSupport extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.refactoringSupport
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleRefactoringSupportProvider"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
The IDE now supports refactoring (https://www.jetbrains.com/help/idea/rename-refactorings.html) suggestions:

Reference: Find Usages
Code: SimpleFindUsagesProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFindUsagesProvider.java)
Testing: 8. Find Usages Test
This page is part of multi step Custom Language Support Tutorial.
A FindUsagesProvider uses a word scanner to build an index of words in every file. A scanner breaks the text into words and defines the context for each word.
The SimpleFindUsagesProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFindUsagesProvider.java) implements FindUsagesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/findUsages/FindUsagesProvider.java). Using the DefaultWordsScanner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java) ensures the scanner implementation is thread-safe. See the comments in FindUsagesProvider for more information.
The SimpleFindUsagesProvider implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.findUsagesProvider extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.findUsagesProvider
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleFindUsagesProvider"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
The IDE now supports Find Usages (https://www.jetbrains.com/help/idea/find-highlight-usages.html) for any property with a reference:

Code: SimpleFoldingBuilder (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFoldingBuilder.java)
Testing: 7. Folding Test
This page is part of multi step Custom Language Support Tutorial.
A folding builder identifies the folding regions in the code. In this step of the tutorial, the folding builder is used to identify folding regions and replace the regions with specific text. Rather than the usual practice of using a folding builder to collapse a class, method, or comments to fewer lines, the folding builder replaces Simple Language keys with their corresponding values.
The SimpleFoldingBuilder (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFoldingBuilder.java) replaces usages of properties with their values by default. Start by subclassing FoldingBuilderEx (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/folding/FoldingBuilderEx.java)
Note that SimpleFoldingBuilder is marked dumb aware ("DumbAware API" in "Indexing and PSI Stubs"), which means the class is allowed to run in dumb mode ("Dumb Mode" in "Indexing and PSI Stubs"), when indexes are in background update.
A folding builder must implement DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) to function in this tutorial and pass tests.
The buildFoldRegions() method searches down a PSI tree from root to find all literal expressions containing the simple prefix ("Define an Annotator" in "7. Annotator") simple:. The remainder of such a string is expected to contain a Simple Language key, and so the text range is stored as a FoldingDescriptor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/folding/FoldingDescriptor.java).
The getPlaceholderText() method retrieves the Simple Language value corresponding to the key associated with the (ASTNode) provided. The IntelliJ Platform uses the value to substitute for the key when the code gets folded.
The SimpleFoldingBuilder implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.foldingBuilder extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.foldingBuilder
language="JAVA"
implementationClass="org.intellij.sdk.language.SimpleFoldingBuilder"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Now when a Java file is opened in the editor, it shows the property's value instead of the key. This is because SimpleFoldingBuilder.isCollapsedByDefault() always returns true. Try using Code | Folding | Expand All to show the key rather than the value.

Reference: Go to Class and Go to Symbol
Code: SimpleChooseByNameContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleChooseByNameContributor.java), SimplePsiImplUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/impl/SimplePsiImplUtil.java)
This page is part of multi step Custom Language Support Tutorial.
A Go to Symbol Contributor helps the user to navigate to any PSI element by its name.
To specify what a PSI element looks like in the Navigate | Symbol popup window, Structure tool window, or other components, it should implement getPresentation(). This method gets defined in the utility class SimplePsiImplUtil, and the parser and PSI classes must be regenerated. Add the following method to SimplePsiImplUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/impl/SimplePsiImplUtil.java):
In addition, to provide an icon for the displayed items, extend IconProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/ide/IconProvider.java) and register it in com.intellij.iconProvider extension point. See SimplePropertyIconProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimplePropertyIconProvider.java):
Now add the SimplePsiImplUtil.getPresentation() to the property methods definition in the Simple.bnf grammar file by replacing the property definition with the lines below. Don't forget to regenerate the parser after updating the file! Right-click on the Simple.bnf file and select Generate Parser Code.
property ::= (KEY? SEPARATOR VALUE?) | KEY {
mixin="org.intellij.sdk.language.psi.impl.SimpleNamedElementImpl"
implements="org.intellij.sdk.language.psi.SimpleNamedElement"
methods=[getKey getValue getName setName getNameIdentifier getPresentation]
}To contribute items to Navigate | Symbol results, subclass ChooseByNameContributorEx (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/navigation/ChooseByNameContributorEx.java) to create SimpleChooseByNameContributor (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleChooseByNameContributor.java):
The SimpleChooseByNameContributor implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.gotoSymbolContributor extension point.
<extensions defaultExtensionNs="com.intellij">
<gotoSymbolContributor
implementation="org.intellij.sdk.language.SimpleChooseByNameContributor"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
The IDE now supports navigating to a property definition by name pattern via Navigate | Symbol action.

Reference: Structure View
Code: SimpleStructureViewFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewFactory.java), SimpleStructureViewModel (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewModel.java), SimpleStructureViewElement (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewElement.java)
This page is part of multi step Custom Language Support Tutorial.
The structure view can be customized for a specific file type. Creating a structure view factory allows showing the structure of any file in the Structure Tool Window for easy navigation between items in the current editor.
The SimpleStructureViewFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewFactory.java) implements PsiStructureViewFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/lang/PsiStructureViewFactory.java). The getStructureViewBuilder() implementation reuses the IntelliJ Platform class TreeBasedStructureViewBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/TreeBasedStructureViewBuilder.java). At this point the project will not compile until SimpleStructureViewModel is implemented below.
The SimpleStructureViewModel (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewModel.java) is created by implementing StructureViewModel (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewModel.java), which defines the model for data displayed in the standard structure view. It also extends StructureViewModelBase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewModelBase.java), an implementation that links the model to a text editor.
The SimpleStructureViewElement (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureViewElement.java) implements StructureViewTreeElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/structureView/StructureViewTreeElement.java) and SortableTreeElement (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/ide/util/treeView/smartTree/SortableTreeElement.java). The StructureViewTreeElement represents an element in the Structure View tree model. The SortableTreeElement represents an item in a smart tree that allows using text other than the presentable text as a key for alphabetic sorting.
The SimpleStructureViewFactory implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.psiStructureViewFactory extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.psiStructureViewFactory
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleStructureViewFactory"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the test.simple file and choose View | Tool Windows | Structure. The IDE now supports a structure view of the Simple Language:

Reference: Navigation Bar
Code: SimpleStructureAwareNavbar (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureAwareNavbar.java)
This page is part of multi step Custom Language Support Tutorial.
Structure-aware navbar allows displaying specific file elements in the navigation bar (https://www.jetbrains.com/help/idea/guided-tour-around-the-user-interface.html#navigation-bar), depending on the location of the caret in it. For example, in Java this is used to display the class and method in which the caret is currently located.
The SimpleStructureAwareNavbar (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleStructureAwareNavbar.java) implements StructureAwareNavBarModelExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/ide/navigationToolbar/StructureAwareNavBarModelExtension.kt).
The SimpleStructureAwareNavbar implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.navbar extension point.
<extensions defaultExtensionNs="com.intellij">
<navbar implementation="org.intellij.sdk.language.SimpleStructureAwareNavbar"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the test.simple file and position the caret on any property. The navigation bar displays the name and icon of this property.

Reference: Code Formatter
Code: SimpleBlock (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleBlock.java), SimpleFormattingModelBuilder (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFormattingModelBuilder.java)
Testing: 5. Formatter Test
This page is part of multi step Custom Language Support Tutorial.
The IntelliJ Platform includes a powerful framework for implementing formatting for custom languages. A formatter enables reformatting code automatically based on code style settings. The formatter controls spaces, indents, wrap, and alignment.
The formatting model represents the formatting structure of a file as a tree of Block (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/Block.java) objects, with associated indent, wrap, alignment, and spacing settings. The goal is to cover each PSI element with such a block. Since each block builds its children's blocks, it can generate extra blocks or skip any PSI elements. Define SimpleBlock (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleBlock.java) based on AbstractBlock (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-impl/src/com/intellij/psi/formatter/common/AbstractBlock.java).
Define a formatter that removes extra spaces except for the single spaces around the property separator:
Create SimpleFormattingModelBuilder (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleFormattingModelBuilder.java) by implementing FormattingModelBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/FormattingModelBuilder.java).
The SimpleFormattingModelBuilder implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.lang.formatter extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.formatter
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleFormattingModelBuilder"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the example Simple Language properties file ("Run the Project" in "4. Lexer and Parser Definition") in the IDE Development Instance. Add some extra spaces around the = separator between language and English. Reformat the code by invoking Code | Reformat File... dialog and choose Run.

Reference: "Code Style Settings" in "Code Formatter"
Code: SimpleCodeStyleSettings (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCodeStyleSettings.java), SimpleCodeStyleSettingsProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCodeStyleSettingsProvider.java), SimpleLanguageCodeStyleSettingsProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLanguageCodeStyleSettingsProvider.java)
This page is part of multi step Custom Language Support Tutorial.
Code style settings enable defining formatting options. A code style settings provider creates an instance of the settings and also creates an options page in Settings. This example creates a Settings page that uses the default language code style settings, customized by a language code style settings provider.
Define SimpleCodeStyleSettings (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCodeStyleSettings.java) for Simple Language by subclassing CustomCodeStyleSettings (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/psi/codeStyle/CustomCodeStyleSettings.java).
The code style settings provider gives the IntelliJ Platform a standard way to instantiate CustomCodeStyleSettings for the Simple Language.
Define SimpleCodeStyleSettingsProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCodeStyleSettingsProvider.java) for Simple Language by subclassing CodeStyleSettingsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsProvider.java).
The SimpleCodeStyleSettingsProvider implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.codeStyleSettingsProvider extension point.
<extensions defaultExtensionNs="com.intellij">
<codeStyleSettingsProvider
implementation="org.intellij.sdk.language.SimpleCodeStyleSettingsProvider"/>
</extensions>Define SimpleLanguageCodeStyleSettingsProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleLanguageCodeStyleSettingsProvider.java) for Simple Language by subclassing LanguageCodeStyleSettingsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-api/src/com/intellij/psi/codeStyle/LanguageCodeStyleSettingsProvider.java), which provides common code style settings for a specific language.
The SimpleLanguageCodeStyleSettingsProvider implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.langCodeStyleSettingsProvider extension point.
<extensions defaultExtensionNs="com.intellij">
<langCodeStyleSettingsProvider
implementation="org.intellij.sdk.language.SimpleLanguageCodeStyleSettingsProvider"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
In the IDE Development Instance, open the Simple Language code formatting page: Settings | Editor | Code Style | Simple.

Reference: "Comment Code" in "Additional Minor Features"
Code: SimpleCommenter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCommenter.java)
Testing: 9. Commenter Test
This page is part of multi step Custom Language Support Tutorial.
A commenter enables the user to comment-out a line of code at the cursor or selected code automatically. The Commenter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Commenter.java) defines support for Code | Comment with Line Comment and Code | Comment with Block Comment actions.
The SimpleCommenter (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCommenter.java) for Simple Language defines the line comment prefix as #.
The SimpleCommenter implementation is registered in the plugin configuration file using the com.intellij.lang.commenter extension point.
<extensions defaultExtensionNs="com.intellij">
<lang.commenter
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleCommenter"/>
</extensions>Run the plugin by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the example Simple Language properties file ("Run the Project" in "4. Lexer and Parser Definition") in the IDE Development Instance. Place the cursor at the website line. Select Code | Comment with Line Comment. The line is converted to a comment. Select Code | Comment with Line Comment again, and the comment is converted back to active code.

Reference: Code Inspections and Intentions
Code: SimpleElementFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleElementFactory.java), SimpleCreatePropertyQuickFix (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCreatePropertyQuickFix.java), SimpleAnnotator (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleAnnotator.java)
This page is part of multi step Custom Language Support Tutorial.
A quick fix for a custom language supports the IntelliJ Platform-based IDE feature Intention Actions (https://www.jetbrains.com/help/idea/intention-actions.html#apply-intention-actions). For the Simple language, this tutorial adds a quick fix that helps to define an unresolved property from its usage.
The SimpleElementFactory (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/psi/SimpleElementFactory.java) is updated to include two new methods to support the user choice of creating a new property for the Simple Language quick fix. The new createCRLF() method supports adding a newline to the end of the test.simple ("Run the Project" in "4. Lexer and Parser Definition") file before adding a new property. A new overload of createProperty() creates a new key-value pair for Simple Language.
The SimpleCreatePropertyQuickFix (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleCreatePropertyQuickFix.java) creates a property in the file chosen by the user - in this case, a Java file containing a prefix:key - and navigate to this property after creation. Under the hood, SimpleCreatePropertyQuickFix is an Intention Action. For a more in-depth example of an Intention Action, see conditional_operator_intention (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/conditional_operator_intention).
When a badProperty annotation is created, the badProperty.registerFix() method in SimpleAnnotator (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleAnnotator.java) is called. This method call registers the SimpleCreatePropertyQuickFix as the Intention Action for the IntelliJ Platform to use to correct the problem.
Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task. Open the test Java file ("Run the Project" in "7. Annotator").
To test SimpleCreatePropertyQuickFix, change simple:website to simple:website.url. The key website.url is highlighted by SimpleAnnotator as an invalid key, as shown below. Choose "Create Property".

The IDE opens the test.simple file and adds website.url as a new key. Add the new value jetbrains.com for the new website.url key.

Now switch back to the Java file; the new key is highlighted as valid.
Reference: Documentation
Code: SimpleDocumentationProvider (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleDocumentationProvider.java), SimpleUtil (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleUtil.java)
Testing: 11. Documentation Test
This page is part of multi step Custom Language Support Tutorial.
For plugins targeting IntelliJ Platform version 2023.1 or later, it is recommended to utilize the Documentation Target API, as detailed in "Documentation Target API" in "Documentation".
A DocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/DocumentationProvider.java) helps users by showing documentation for symbols like method calls inside the editor. For the custom language tutorial, we're implementing a version of this extension point (EP) for the Simple Language that shows the key/value, the file where it is defined, and any related documentation comment.
In the first step, we create an empty class that extends AbstractDocumentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/documentation/AbstractDocumentationProvider.java) and register it in the plugin.xml (Plugin Configuration File).
final class SimpleDocumentationProvider extends AbstractDocumentationProvider { }Make sure the class is registered in the plugin.xml between the extensions tags, as shown below:
<extensions defaultExtensionNs="com.intellij">
<!-- Other extensions… -->
<lang.documentationProvider
language="Simple"
implementationClass="org.intellij.sdk.language.SimpleDocumentationProvider"/>
</extensions>For the Simple Language, we consider two use-cases:
A Simple key is used inside a Java string literal (10. Reference Contributor), and we would like to show documentation for the key/value right from the reference inside the Java file.
The cursor is already over a key/value definition inside a Simple file, in which case we would also like to show its documentation.
To ensure that the IntelliJ Platform chooses the correct element of type SimpleProperty when View | Quick Documentation is called, we create a dummy implementation of generateDoc():
@Override
public @Nullable String generateDoc(PsiElement element,
@Nullable PsiElement originalElement) {
return super.generateDoc(element, originalElement);
}Now, we set a breakpoint in our dummy implementation, debug the plugin, and call View | Quick Documentation for the Simple property both in the Java file and the Simple file. We do this by placing the cursor over the key and invoking View | Quick Documentation to show the documentation.
In both cases, we find that the element provided is SimplePropertyImpl, which is exactly what we hoped for. However, there are two drawbacks: inside a Java string, your cursor needs to be directly over key in the string "simple:key" to make Quick Documentation work. Since the Simple Language only allows for one property per string, it would be nice if Quick Documentation worked no matter where your cursor was positioned in the string as long as the string contained a Simple property. Inside a Simple file, the situation is similar, and calling View | Quick Documentation only works when the cursor is positioned on the key.
Please refer to the Addendum below, which explains how to improve on this situation by additionally overriding getCustomDocumentationElement() method.
While SimpleProperty elements will provide us with their key and value, we have no direct access to a possible comment that is preceding the key/value definition. Since we would like to show this comment in the documentation as well, we need a small helper function that extracts the text from the comment. This function will reside in the SimpleUtil class and will find, for instance, the comment preceding apikey in the following short example:
#An application programming interface key (API key) is a unique identifier
#used to authenticate a user, developer, or calling program to an API.
apikey=ph342m91337h4xX0r5k!11Zz!The following implementation will check if there is any comment preceding a SimpleProperty, and if there is, it will collect all comment lines until it reaches either the previous key/value definition or the beginning of the file. One caveat is that since we're collecting the comment lines backwards, we need to reverse the list before joining them into a single string. A simple regex is used to remove the leading hash characters and whitespaces from each line.
With easy ways to access the key, the value, the file, and a possible documentation comment, we now have everything in place to provide a useful implementation of generateDoc().
The creation of the rendered documentation is done in a separate method for clarity. It uses DocumentationMarkup to align and format the contents.
The addKeyValueSection() method used is just a small helper function to reduce repetition.
After implementing all the steps above, the IDE will show the rendered documentation for a Simple key when called with View | Quick Documentation.
We can provide implementations for additional functionality that comes with a DocumentationProvider. For instance, when simply hovering the mouse over the code, it also shows documentation after a short delay. It's not necessary that this popup shows the exact same information as when calling Quick Documentation, but for the purpose of this tutorial, we'll do just that.
When the mouse hovers over code with Ctrl/Cmd pressed, the IDE shows navigation information of the symbol under the cursor, such as its namespace or package. The implementation below will show the Simple key and the file where it is defined.
Finally, View | Quick Documentation can also be called from a selected entry within the autocompletion popup. In that case, language developers need to ensure that the correct PSI element for generating the documentation is provided. In the case of Simple Language, the lookup element is already a SimpleProperty and no additional work needs to be done. In other circumstances, you can override getDocumentationElementForLookupItem() and return the correct PSI element.
To be able to call View | Quick Documentation for Simple properties in all places of a Java string literal, two steps are required:
The extension point needs to be changed from lang.documentationProvider to documentationProvider because only then the Simple DocumentationProvider is called for PSI elements with a different language.
The getCustomDocumentationElement() method needs to be implemented to find the correct target PSI element for creating the documentation.
Therefore, the current version of the code could be extended to check whether View | Quick Documentation was called from inside a Java string or a Simple file. It then uses PSI and PsiReference functionalities to determine the correct target element. This allows getting documentation for a Simple property no matter where it was called inside a Java string literal or a Simple property definition.
@Override
public @Nullable PsiElement getCustomDocumentationElement(@NotNull Editor editor,
@NotNull PsiFile file,
@Nullable PsiElement context,
int targetOffset) {
if (context != null) {
// In this part the SimpleProperty element
// is extracted from inside a Java string
if (context instanceof PsiJavaToken &&
((PsiJavaToken) context).getTokenType().equals(JavaTokenType.STRING_LITERAL)) {
PsiElement parent = context.getParent();
PsiReference[] references = parent.getReferences();
for (PsiReference ref : references) {
if (ref instanceof SimpleReference) {
PsiElement property = ref.resolve();
if (property instanceof SimpleProperty) {
return property;
}
}
}
}
// In this part the SimpleProperty element is extracted
// when inside of a .simple file
else if (context.getLanguage() == SimpleLanguage.INSTANCE) {
PsiElement property =
PsiTreeUtil.getParentOfType(context, SimpleProperty.class);
if (property != null) {
return property;
}
}
}
return super.getCustomDocumentationElement(
editor, file, context, targetOffset);
}Reference: Spell Checking
Code: SimpleSpellcheckingStrategy (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSpellcheckingStrategy.java)
This page is part of multi step Custom Language Support Tutorial.
Spell checking allows users to see spelling errors while editing code.
The SimpleSpellcheckingStrategy (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/simple_language_plugin/src/main/java/org/intellij/sdk/language/SimpleSpellcheckingStrategy.java) extends SpellcheckingStrategy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/com/intellij/spellchecker/tokenizer/SpellcheckingStrategy.java)
The implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.intellij.spellchecker.support extension point.
<extensions defaultExtensionNs="com.intellij">
<spellchecker.support language="Simple" implementationClass="org.intellij.sdk.language.SimpleSpellcheckingStrategy"/>
</extensions>Run the project by using the Gradle runIde ("Running a Plugin With the runIde Gradle task" in "Creating a Plugin Gradle Project") task.
Open the test.simple file and make an intentional spelling mistake. The IDE will highlight the error and suggest a quick fix.

Please see Testing Overview for a general introduction.
This tutorial demonstrates how to write and run automated tests for a custom language plugin.
As an example, the plugin implemented in the Custom Language Support Tutorial (Custom Language Support Tutorial) is used to demonstrate functional test development.
Corresponding parts are linked under Tested Functionality on top of each page in this tutorial.
This page is part of multistep Testing a Custom Language Plugin tutorial.
This page discusses the steps to configure a plugin project for creating tests.
Open the plugin project and create a separate folder named test under the src directory. Under test, create the java folder for test source code, and the folder testData for test data files (Test Project and Testdata Directories) and reimport the Gradle project.
Because some tests use Java files as test data, the tests need to mock up the project's SDK. See the "How to test a JVM language?" in "Testing FAQ" section for details.
Tested Functionality: 4. Lexer and Parser Definition
This page is part of multistep Testing a Custom Language Plugin tutorial.
For more complex Lexers (e.g., having additional logic), it is advisable to add separate tests inheriting from LexerTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/LexerTestCase.java).
Before creating the parsing test, ensure the parser definition (Simple.bnf) includes the lines shown below. These additional lines facilitate testing incorrect keys.
If the lines below are not present in Simple.bnf, replace the existing property definition with the lines below. Don't forget to regenerate the parser after updating the file! Right-click on the Simple.bnf file and select Generate Parser Code.
property ::= (KEY? SEPARATOR VALUE?) | KEY {
pin=3
recoverWhile="recover_property"
mixin="org.intellij.sdk.language.psi.impl.SimpleNamedElementImpl"
implements="org.intellij.sdk.language.psi.SimpleNamedElement"
methods=[getKey getValue getName setName getNameIdentifier getPresentation]
}
private recover_property ::= !(KEY|SEPARATOR|COMMENT)Create the ParsingTestData.simple properties file in the testData folder. Note the last few lines define a purposely incorrect key.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009
# test for illegal key attempt
key\
with\
endofline = testThe easiest way to get the expected PSI structure for any file is to use the PSI Viewer. Run the project and use Tools | View PSI Structure.

Use the Copy PSI button to copy the expected PSI structure to the clipboard.
Create a file ParsingTestData.txt with the copied PSI tree.
Simple File(0,493)
PsiComment(SimpleTokenType.COMMENT)('# You are reading the ".properties" entry.')(0,42)
PsiWhiteSpace('\n')(42,43)
PsiComment(SimpleTokenType.COMMENT)('! The exclamation mark can also mark text as comments.')(43,97)
PsiWhiteSpace('\n')(97,98)
SimplePropertyImpl(PROPERTY)(98,133)
PsiElement(SimpleTokenType.KEY)('website')(98,105)
PsiWhiteSpace(' ')(105,106)
PsiElement(SimpleTokenType.SEPARATOR)('=')(106,107)
PsiWhiteSpace(' ')(107,108)
PsiElement(SimpleTokenType.VALUE)('https://en.wikipedia.org/')(108,133)
PsiWhiteSpace('\n\n')(133,135)
SimplePropertyImpl(PROPERTY)(135,153)
PsiElement(SimpleTokenType.KEY)('language')(135,143)
PsiWhiteSpace(' ')(143,144)
PsiElement(SimpleTokenType.SEPARATOR)('=')(144,145)
PsiWhiteSpace(' ')(145,146)
PsiElement(SimpleTokenType.VALUE)('English')(146,153)
PsiWhiteSpace('\n')(153,154)
PsiComment(SimpleTokenType.COMMENT)('# The backslash below tells the application to continue reading')(154,217)
PsiWhiteSpace('\n')(217,218)
PsiComment(SimpleTokenType.COMMENT)('# the value onto the next line.')(218,249)
PsiWhiteSpace('\n')(249,250)
SimplePropertyImpl(PROPERTY)(250,293)
PsiElement(SimpleTokenType.KEY)('message')(250,257)
PsiWhiteSpace(' ')(257,258)
PsiElement(SimpleTokenType.SEPARATOR)('=')(258,259)
PsiWhiteSpace(' ')(259,260)
PsiElement(SimpleTokenType.VALUE)('Welcome to \\n Wikipedia!')(260,293)
PsiWhiteSpace('\n')(293,294)
PsiComment(SimpleTokenType.COMMENT)('# Add spaces to the key')(294,317)
PsiWhiteSpace('\n')(317,318)
SimplePropertyImpl(PROPERTY)(318,411)
PsiElement(SimpleTokenType.KEY)('key\ with\ spaces')(318,335)
PsiWhiteSpace(' ')(335,336)
PsiElement(SimpleTokenType.SEPARATOR)('=')(336,337)
PsiWhiteSpace(' ')(337,338)
PsiElement(SimpleTokenType.VALUE)('This is the value that could be looked up with the key "key with spaces".')(338,411)
PsiWhiteSpace('\n')(411,412)
PsiComment(SimpleTokenType.COMMENT)('# Unicode')(412,421)
PsiWhiteSpace('\n')(421,422)
SimplePropertyImpl(PROPERTY)(422,434)
PsiElement(SimpleTokenType.KEY)('tab')(422,425)
PsiWhiteSpace(' ')(425,426)
PsiElement(SimpleTokenType.SEPARATOR)(':')(426,427)
PsiWhiteSpace(' ')(427,428)
PsiElement(SimpleTokenType.VALUE)('\u0009')(428,434)
PsiWhiteSpace('\n')(434,435)
PsiComment(SimpleTokenType.COMMENT)('# test for illegal key attempt')(435,465)
PsiWhiteSpace('\n')(465,466)
SimplePropertyImpl(PROPERTY)(466,470)
PsiElement(SimpleTokenType.KEY)('key')(466,469)
PsiErrorElement:SimpleTokenType.SEPARATOR expected, got '\'(469,470)
PsiElement(BAD_CHARACTER)('\')(469,470)
PsiWhiteSpace('\n')(470,471)
SimplePropertyImpl(PROPERTY)(471,476)
PsiElement(SimpleTokenType.KEY)('with')(471,475)
PsiErrorElement:SimpleTokenType.SEPARATOR expected, got '\'(475,476)
PsiElement(BAD_CHARACTER)('\')(475,476)
PsiWhiteSpace('\n')(476,477)
SimplePropertyImpl(PROPERTY)(477,493)
PsiElement(SimpleTokenType.KEY)('endofline')(477,486)
PsiWhiteSpace(' ')(486,487)
PsiElement(SimpleTokenType.SEPARATOR)('=')(487,488)
PsiWhiteSpace(' ')(488,489)
PsiElement(SimpleTokenType.VALUE)('test')(489,493)Subclass ParsingTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/ParsingTestCase.java) to create SimpleParsingTest: Override getTestDataPath(), and return the path from the root of this plugin module to the testData directory.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.intellij.sdk.language;
import com.intellij.testFramework.ParsingTestCase;
public class SimpleParsingTest extends ParsingTestCase {
public SimpleParsingTest() {
super("", "simple", new SimpleParserDefinition());
}
public void testParsingTestData() {
doTest(true);
}
/**
* @return path to test data file directory relative to root of this module.
*/
@Override
protected String getTestDataPath() {
return "src/test/testData";
}
@Override
protected boolean includeRanges() {
return true;
}
}Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 10. Reference Contributor
This page is part of multistep Testing a Custom Language Plugin tutorial.
Create the DefaultTestData.simple file in the testData directory.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009Create a test input Java file CompleteTestData.java in the testData directory. This file contains a Simple Language reference within the Java code at <caret> special marker ("Special Markup" in "Test Project and Testdata Directories"), which denotes the caret position to use in the test.
public class Test {
public static void main(String[] args) {
System.out.println("simple:<caret>");
}
}Subclass LightJavaCodeInsightFixtureTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase.java) to create SimpleCodeInsightTest. Override getTestDataPath(), and return the path from the root of this plugin module to the testData directory.
At this point only one test is defined in SimpleCodeInsightTest: testCompletion(). This method:
Configures the test using the two input files.
Calls the basic completion functionality. Behind the scenes, this method call creates a list of possible elements to complete the embedded Simple Language reference.
Checks the list of returned lookup strings to ensure it matches the completion variants provided by the reference.
A number of related methods exist in CodeInsightTestFixture (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java) for testing completion and lookup elements, e.g., when testing completion variants and requiring only one testdata file CodeInsightTestFixture.testCompletionVariants().
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 7. Annotator
This page is part of multistep Testing a Custom Language Plugin tutorial.
The DefaultTestData.simple file is reused for this test.
Create an input test file AnnotatorTestData.java in the testData directory. This file contains two instances of Simple Language embedded in the Java code. The first instance is a valid use of the simple: prefix followed by the Simple Language key website. The second is a valid prefix but an invalid key websit, as noted by the test <error> highlighting (Testing Highlighting).
public class Test {
public static void main(String[] args) {
System.out.println("simple:website");
System.out.println("simple:<error descr="Unresolved property">websit</error>");
}
}Add the testAnnotator() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). Again, this method configures the test fixture by using the test files. It then calls the checkHighlighting() method to verify weak warnings.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 16. Formatter
This page is part of multistep Testing a Custom Language Plugin tutorial.
See also FormatterTestCase (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/testFramework/src/com/intellij/psi/formatter/FormatterTestCase.java) as convenient base class.
Create the FormatterTestData.simple file in the testData directory.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website=https://en.wikipedia.org/
language= English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab :\u0009Add the testFormatter() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test").
Again, this method configures the test fixture by using the test file.
The code style Simple Language settings for spaces and blank lines are set.
The file is then formatted according to the settings.
The formatted file is compared to the expected results in the benchmark file DefaultTestData.simple.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 10. Reference Contributor
This page is part of multistep Testing a Custom Language Plugin tutorial.
Create the RenameTestData.simple properties file in the testData directory.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009Create the file RenameTestData.java in the testData directory. This file contains one Simple Language reference embedded in Java, with the caret position ("Special Markup" in "Test Project and Testdata Directories") placed just after a Simple Language key.
public class Test {
public static void main(String[] args) {
System.out.println("simple:website<caret>");
}
}Create the RenameTestDataAfter.simple file in the testData directory. This file contains the expected outcome of the test. Note the website = in RenameTestData.simple should be renamed to websiteUrl = by the test.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
websiteUrl = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009Add the testRename() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test").
Again, this method configures the test fixture by using the test files.
The fixture then renames the Simple Language element at the caret in RenameTestData.java.
It then compares the input and output files, ignoring whitespace.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 12. Folding Builder
This page is part of multistep Testing a Custom Language Plugin tutorial.
A folding builder must implement DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) to pass tests. See "Define a Folding Builder" in "12. Folding Builder" for more information.
Create a file FoldingTestData.java in the testData directory. This java file contains markup instructions for three different cases of code folding.
public class Test {
public static void main(String[] args)<fold text=' { '> {
</fold>System.out.println("<fold text='https://en.wikipedia.org/'>simple:website</fold>");<fold text=' }'>
}</fold>
public static void main1(String[] args)<fold text=' { '> {
</fold>System.out.println("<fold text='This is the value that could be looked up with the key \"key with spaces\".'>simple:key with spaces</fold>");<fold text=' }'>
}</fold>
public static void main2(String[] args)<fold text=' { '> {
</fold>System.out.println("<fold text='Welcome to \n Wikipedia!'>simple:message</fold>");<fold text=' }'>
}</fold>
}Add the testFolding() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). This test method reuses the DefaultTestData.simple Simple file.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 11. Find Usages Provider
This page is part of multistep Testing a Custom Language Plugin tutorial.
Create the FindUsagesTestData.simple file in the testData directory.
# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
Wikipedia!
# Add spaces to the key
<caret>key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009Create the test file FindUsagesTestData.java, which contains one embedded Simple Language prefix and key.
public class Test {
public static void main(String[] args) {
System.out.println("simple:key with spaces");
}
}Add the testFindUsages() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). This test verifies the find usage functionality will identify the "key with spaces".
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 18. Commenter
This page is part of multistep Testing a Custom Language Plugin tutorial.
Add the testCommenter() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). This test constructs a Simple Language properties file containing one line, with the virtual caret positioned at the beginning of the line. The test calls the commenter to insert a comment character at the caret, then verifies the results. It again calls the line comment action to remove the comment character and verifies the results.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 10. Reference Contributor
This page is part of multistep Testing a Custom Language Plugin tutorial.
This test reuses the Simple Language file DefaultTestData.simple.
Create the test file ReferenceTestData.java in the testData directory. This file has one Simple Language prefix and key, with the caret placed after the key.
public class Test {
public static void main(String[] args) {
System.out.println("simple:website<caret>");
}
}Add the testReference() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). This test is configured by the test files. The fixture gets the PsiReference at the caret position, and then asserts the resolved SimpleProperty.value() with the known value of that key.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Tested Functionality: 20. Documentation
This page is part of multistep Testing a Custom Language Plugin tutorial.
Add the testDocumentation() method to the SimpleCodeInsightTest class previously defined ("Define a Test" in "3. Completion Test"). This test uses the files DocumentationTestData.java and DocumentationTestData.simple from the testData directory to construct a test project and opens the first one in the editor with the virtual caret positioned at the usage of the Simple Language property.
Using the DocumentationManager, it checks if a target element for documentation is found, retrieves the documentation provider for the Simple Language and creates the documentation string for the target element. Finally, the documentation string is verified against the expected output.
Run the test and make sure it's successful.
Open the Gradle Tool Window.
Select the simple_language_plugin node. You may need to reimport it as a Gradle project.
Drill down under simple_language_plugin to Tasks, verification, test task.
Run the test task.
The results are displayed in the Run Tool Window, and also written to the simple_language_plugin/build/test-results/test directory.
If the Run Tool Window displays the error Test events were not received, do the following:
In the Gradle Tool Window, drill down under simple_language_plugin to Tasks, build, clean task.
Run the clean task, which deletes the simple_language_plugin/build directory.
Restart the test.
Product Help: Language injections (https://www.jetbrains.com/help/idea/language-injections-settings.html)
Language injection is the way the IntelliJ Platform handles different languages within the same source file. Well-known examples are:
Regular expressions in Java string literals
SQL queries in Java string literals
Fenced code blocks within Markdown files
Injected code is always bound to a specific context that depends on the surrounding code, and the IntelliJ Platform treats injected fragments as separate small files that are in a different language. To ensure highlighting and code-insight features work correctly, these fragments must be a valid statement or expression in the injected language. The three examples from above would then be shown like this in IntelliJ IDEs:



It's not unusual that injected fragments are distributed among, e.g., several strings that are concatenated like it is common for SQL queries. To solve this, the IntelliJ Platform allows injecting a language into several fragments at once. Multiple parts are then considered belonging together.
As a plugin author, you can provide language injection in different ways:
For simple cases, the bundled IntelliLang plugin (https://plugins.jetbrains.com/plugin/13374-intellilang) can handle injections, and plugin authors need to provide a configuration with patterns that specify the context where languages should be injected. IntelliLang can also be extended to support unknown custom languages.
Implementing the com.intellij.languageInjectionContributor extension point (EP) provides a high-level API for the injection of other languages. For more control over how a language is injected, plugin authors use the com.intellij.languageInjectionPerformer EP.
Implementing the com.intellij.multiHostInjector EP gives plugin authors the most control over where and how language injection will take place.
In the following sections, we'll discuss these three options in more detail.
First, please read the available documentation (https://www.jetbrains.com/help/idea/language-injections-settings.html) on IntelliLang. A good point to start with is to inspect available language injections that you can find in the IntelliLang settings under Settings | Editor | Language Injections. The injections shown are configured through XML files and loaded automatically.
Let's take a look at the Java String.matches() method that injects the RegExp language into the string of the first argument. In the IntelliLang settings, it is defined as one possible injection in Java code.

Double-clicking on this entry shows the exact context where a RegExp can be injected, and String.matches() is one of several possibilities. On the plugin side, these entries are defined in the file javaInjections.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/java-support/resources/javaInjections.xml):
<injection language="RegExp" injector-id="java">
<display-name>String (java.lang)</display-name>
...
<place><![CDATA[
psiParameter()
.ofMethod(0, psiMethod().withName("matches")
.withParameters("java.lang.String")
.definedInClass("java.lang.String"))
]]></place>
</injection>The XML file with the injection configurations is loaded through the org.intellij.intelliLang.injectionConfig EP in the file intellilang-java-support.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/META-INF/intellilang-java-support.xml).
<extensions defaultExtensionNs="org.intellij.intelliLang">
<languageSupport
implementation="org.intellij.plugins.intelliLang.inject.java.JavaLanguageInjectionSupport"/>
<injectionConfig config="resources/javaInjections.xml"/>
</extensions>It is important to make a distinction between plugin authors who want to provide injections into existing languages and plugin authors who want to provide support for IntelliLang injections in their custom language. Both define their injections by providing XML configurations and loading them through the plugin.xml (Plugin Configuration File). However, custom language authors need to implement the org.intellij.intelliLang.languageSupport EP to make their language and PSI element patterns known to IntelliLang. Therefore, plugin authors who want to provide injections for existing languages can skip the first step.
Implement the org.intellij.intelliLang.languageSupport EP and use AbstractLanguageInjectionSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/AbstractLanguageInjectionSupport.java) as a base class. Please refer to the API docs of LanguageInjectionSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java) for information on methods to override and use JavaLanguageInjectionSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java) as an example implementation.
Create an XML file with the injection configuration. You can export existing injections from the IntelliLang settings to create a template and then edit it. Element Patterns are used to specify the context where injections will take place. Custom language authors can use the specific patterns returned from their implementation of JavaLanguageInjectionSupport.getPatternClasses (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java).
The injection tag requires the attributes language and injector-id. The first one specifies the language-id (see Language.getID() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/Language.java)) of the language that is injected. The second one is the id of the host language (see JavaLanguageInjectionSupport.getId() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/java-support/org/intellij/plugins/intelliLang/inject/java/JavaLanguageInjectionSupport.java)). For instance, injecting SQLite into Python code is specified by the following opening tag:
<injection language="SQLite" injector-id="python">
...
</injection>Inside an injection, the following tags can be used:
XML Tag | Description |
|---|---|
<display-name> | A short name for the injection. |
<place> | The element pattern that defines where an injection will take place. The content is wrapped in ![CDATA[...]]. |
<prefix> and <suffix> | Static content that is wrapped around the injected code, e.g., to make it a valid expression. For example, to a CSS color specification inside a string, it can be wrapped with the prefix div { color: and the suffix ;} to make it a valid CSS expression. |
<value-pattern> | A regex for the content that specifies when this injection should be applied. Regex groups can specify the text range of the injection (e.g. ^javascript:(.+), see xmlInjections-html.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/xml-support/resources/xmlInjections-html.xml)). |
<ignore-pattern> | A regex for the content that specifies when this injection should not be applied. |
Create an XML file myLanguageID-injections.xml next to your plugin.xml that loads the above configuration. Custom language authors also register their implementation of the languageSupport EP there.
<idea-plugin>
<extensions defaultExtensionNs="org.intellij.intelliLang">
<injectionConfig config="path/to/your/injections.xml"/>
</extensions>
</idea-plugin>The injections are an optional dependency that only works when IntelliLang is enabled. Therefore, you load the configuration optionally in your main plugin.xml:
<depends
optional="true"
config-file="myLanguageID-injections.xml">org.intellij.intelliLang</depends>The com.intellij.languageInjectionContributor EP provides injection information for the given context in terms of what to inject. As a plugin author, implement LanguageInjectionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/injection/general/LanguageInjectionContributor.java) to provide context-specific injections.
For instance, if you want to inject a YAML or JSON to a literal language depending on some conditions, you could implement this interface like this:
final class MyInjector implements LanguageInjectionContributor {
@Override
public @Nullable Injection getInjection(@NotNull PsiElement context) {
if (!isConfigPlace(context)) return null;
if (shouldInjectYaml(context)) {
return new SimpleInjection(
YAMLLanguage.INSTANCE, "", "", null
);
} else if (shouldInjectJSON(context)) {
return new SimpleInjection(
JsonLanguage.INSTANCE, "", "", null
);
}
return null;
}
}Register the implementation in your plugin.xml:
<languageInjectionContributor
implementationClass="MyInjector"
language="YourLanguage"/>If you want more control over how the injection should be done then implement the com.intellij.languageInjectionPerformer EP which allows for complex language injections, e.g. for concatenation or interpolation of strings. If it is not implemented, then the DefaultLanguageInjectionPerformer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/DefaultLanguageInjectionPerformer.java) will be used.
For the com.intellij.languageInjectionPerformer EP, two methods need to be implemented in LanguageInjectionPerformer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/injection/general/LanguageInjectionPerformer.java). First, isPrimary() determines if this is the default LanguageInjectionPerformer for the language and if it handles most of the injections. If there is no primary LanguageInjectionPerformer found, then a fallback injection will be performed.
The method performInjection() does the actual injection into the context PSI element and/or some elements around it if needed in case if they are semantically connected (concatenation injection for instance).
To use Language Injection API in your project, add dependency ("2. Project Setup" in "Plugin Dependencies") on the org.intellij.intelliLang plugin.
MultiHostInjector (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/injection/MultiHostInjector.java) registered in com.intellij.multiHostInjector EP is a very low-level API, but it gives plugin authors the most freedom. It performs language injection inside other PSI elements, e.g. inject SQL inside an XML tag text or inject regular expressions into Java string literals.
Plugin authors need to implement getLanguagesToInject() to provide a list of places to inject a language, and elementsToInjectIn() to return a list of elements to inject.
For example, inject regular expressions into Java string literal:
final class MyRegExpToJavaInjector implements MultiHostInjector {
@Override
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar,
@NotNull PsiElement context) {
if (context instanceof PsiLiteralExpression && shouldInject(context)) {
registrar
.startInjecting(RegExpLanguage.INSTANCE)
.addPlace(null, null,
(PsiLanguageInjectionHost)context,
innerRangeStrippingQuotes(context))
.doneInjecting();
}
}
@Override
public @NotNull List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
return List.of(PsiLiteralExpression.class);
}
}Register the implementation in your plugin.xml:
<multiHostInjector
implementation="MyRegExpToJavaInjector"/>A more complex example is when you need to inject into several fragments at once. For example, if we have an XML-based DSL:
<myDSL>
<method>
<name>foo</name>
<body>System.out.println(42);</body>
</method>
</myDSL>which should be converted to the equivalent Java code:
class MyDsl { void foo() { System.out.println(42); } }Here, we need to inject Java into several places at once, i.e. method name and its body:
final class MyDSLInjector implements MultiHostInjector {
@Override
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar,
@NotNull PsiElement context) {
if (context instanceof XmlText && isMethodTag(context)) {
registrar.startInjecting(JavaLanguage.INSTANCE);
// construct class header, method header,
// inject method name, append code block start
registrar.addPlace("class MyDsl { void ", "() {",
(PsiLanguageInjectionHost)context,
rangeForMethodName(context));
// inject method body, append closing braces
// to form a valid Java class structure
registrar.addPlace(null, "}}", context, rangeForBody(context));
registrar.doneInjecting();
}
}
@Override
public @NotNull List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
return List.of(XmlText.class);
}
}Now, inside the editor the injected portion will work as expected where foo is the method name and System.out.println(42); will look and feel like a method body with highlighting, completion, and goto definition working.
To control delegation of formatting to containing file, implement InjectedFormattingOptionsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-api/src/com/intellij/formatting/InjectedFormattingOptionsProvider.java) and register in com.intellij.formatting.injectedOptions extension point (2022.3).
To suppress highlighting from Code | Injected language fragment setting in Settings | Editor | Color Scheme | General, injection host must implement InjectionBackgroundSuppressor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/psi/impl/source/tree/injected/InjectionBackgroundSuppressor.java).
The Language Server Protocol (https://microsoft.github.io/language-server-protocol/) (LSP) is an open-standard protocol developed by Microsoft. It enables communication between development tools and Language Servers. Language Servers can provide language-specific features such as code completion, documentation, and formatting, which is far easier than implementing language support from scratch. It also reduces the need for constant maintenance and tracking of changes in relevant languages and tools, making it easier to bring consistent language support to various development environments.
However, the canonical Custom Language Support provided by IntelliJ Platform still offers a wider range of integration with IDE features than handling and presenting data provided by a Language Server. Therefore, the LSP approach shouldn't be considered as a replacement for the existing language API, but rather as an added value.
The integration with the Language Server Protocol is created as an extension to the paid IntelliJ-based IDEs. Therefore, plugins using Language Server integration are not available in Community releases of JetBrains products and Android Studio from Google.
Starting with the 2023.2 release cycle, the LSP API is publicly available as part of the IntelliJ Platform in the following IDEs: IntelliJ IDEA Ultimate, WebStorm, PhpStorm, PyCharm Professional, DataSpell, RubyMine, CLion, Aqua, DataGrip, GoLand, Rider, and RustRover.
To use the LSP API in a third-party plugin based on the Gradle IntelliJ Plugin, it is required to upgrade the Gradle IntelliJ Plugin to the latest version available. This plugin will attach the LSP API sources and code documentation to the project.
As LSP became available in the 2023.2 EAP7 of IntelliJ-based IDEs, the plugin must target IntelliJ IDEA Ultimate version 2023.2 or later.
Example build.gradle.kts configuration:
plugins {
// ...
id("org.jetbrains.intellij") version "1.17.3"
}
intellij {
version = "2024.1"
type = "IU"
}For projects based on the IntelliJ Platform Plugin Template, update the Gradle IntelliJ Plugin to the latest version, and amend the gradle.properties file as follows:
platformType = IU
platformVersion = 2024.1The plugin.xml configuration file needs to specify the dependency on the IntelliJ IDEA Ultimate module:
<idea-plugin>
<!-- ... -->
<depends>com.intellij.modules.ultimate</depends>
</idea-plugin>The LSP API sources are bundled in IntelliJ IDEA Ultimate and can be found within the [IDE]/lib/src/src_lsp-openapi.zip archive.
Attaching Sources in IDE before 2024.1Due to technical limitations in IDEs before 2024.1, it is necessary to manually attach sources to the IntelliJ IDEA Ultimate dependency. To do so, when reviewing the compiled class which belongs to the LSP API, run the Choose Sources... action, and point to the [IDE]/lib/src/src_lsp-openapi.zip file.
The LSP support provided by the IntelliJ Platform covers the following features for these releases:
Errors and warnings highlighting textDocument/publishDiagnostics (https://microsoft.github.io/language-server-protocol/specification/#textDocument_publishDiagnostics)
Quick fixes for errors and warnings (textDocument/codeAction (https://microsoft.github.io/language-server-protocol/specification/#textDocument_codeAction))
Code completion (textDocument/completion (https://microsoft.github.io/language-server-protocol/specification/#textDocument_completion))
Go to Declaration (textDocument/definition (https://microsoft.github.io/language-server-protocol/specification/#textDocument_definition))
Intention actions (textDocument/codeAction (https://microsoft.github.io/language-server-protocol/specification/#textDocument_codeAction))
Code formatting (textDocument/formatting (https://microsoft.github.io/language-server-protocol/specification/#textDocument_formatting))
Quick documentation (textDocument/hover (https://microsoft.github.io/language-server-protocol/specification#textDocument_hover))
As a reference, check out the Prisma ORM (https://plugins.jetbrains.com/plugin/20686-prisma-orm) open-source plugin implementation: Prisma ORM LSP (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/prisma/src/org/intellij/prisma/ide/lsp)
Implement LspServerSupportProvider and within the LspServerSupportProvider.fileOpened() method, spin up the relevant LSP server descriptor, which can decide if the given file is supported by using the LspServerDescriptor.isSupportedFile() check method.
Register ("Declaring Extensions" in "Extensions") it as a com.intellij.platform.lsp.serverSupportProvider Extension Point (EP) (Extension Points):
Tell how to start the server by implementing LspServerDescriptor.createCommandLine().
import com.intellij.platform.lsp.api.LspServerSupportProvider
import com.intellij.platform.lsp.api.ProjectWideLspServerDescriptor
internal class FooLspServerSupportProvider : LspServerSupportProvider {
override fun fileOpened(project: Project, file: VirtualFile, serverStarter: LspServerStarter) {
if (file.extension == "foo") {
serverStarter.ensureServerStarted(FooLspServerDescriptor(project))
}
}
}
private class FooLspServerDescriptor(project: Project) : ProjectWideLspServerDescriptor(project, "Foo") {
override fun isSupportedFile(file: VirtualFile) = file.extension == "foo"
override fun createCommandLine() = GeneralCommandLine("foo", "--stdio")
}Since 2024.1, a dedicated Language Services status bar widget is available to monitor the status of all LSP servers. Override LspServerSupportProvider.createLspServerWidgetItem() to provide a custom icon and link to Settings (Settings) page (if available).
override fun getLspServerWidgetItem(lspServer: LspServer,
currentFile: VirtualFile?) =
LspServerWidgetItem(lspServer, currentFile,
FooIcons.PluginIcon, FooConfigurable::class.java)If there are configuration problems preventing from starting an LSP server, the plugin can provide a widget item with an error and give the user a hint how to fix the problem.
Language Server is a separate process that analyzes source code and provides language-specific features to development tools. When creating a plugin that uses LSP within the IDE, there are two possibilities for providing a Language Server to end-users:
Bundle a Language Server implementation (https://microsoft.github.io/language-server-protocol/implementors/servers/) binary as a resource delivered with a plugin.
Provide a possibility for users to define the location of the Language Server binary in their environment.
The Prisma ORM plugin presents the first approach, which distributes the prisma-language-server.js script and uses a local Node.js interpreter to run it.
For more complex cases, the plugin may request to provide a detailed configuration with a dedicated Settings (Settings Guide) implementation.
To fine-tune or disable the implementation of LSP-based features, plugins may override the corresponding properties of the LspServerDescriptor class. See the property documentation for more details.
Since 2023.2:
lspGoToDefinitionSupport
lspCompletionSupport
lspDiagnosticsSupport
lspCodeActionsSupport
lspCommandsSupport
Since 2023.3:
lspFormattingSupport
lspHoverSupport
To handle custom (undocumented) requests and notifications from the LSP server, override LspServerDescriptor.createLsp4jClient property and the Lsp4jClient class according to their documentation.
To send custom (undocumented) requests and notifications to the LSP server, override LspServerDescriptor.lsp4jServerClass property and implement the LspClientNotification and/or LspRequest classes. The documentation in the source code includes implementation examples.
See bundled LSP API source code and its documentation for more information.
All the IDE and LSP server communication logs are passed to the IDE log file.
To include them, add the following entry to the Help | Diagnostic Tools | Debug Log Settings… configuration dialog:
#com.intellij.platform.lspFor more information, see the "Logging" in "IDE Infrastructure" section.
Integrating the Language Server Protocol (LSP) into a plugin for IntelliJ-based IDEs involves a trade-off between simple and fast language support and a complex custom language support plugin with IDE capabilities.
When considering the LSP-based approach, it is important to assess the following criteria for providing a Language Server to end users:
OS dependency of the Language Server.
Availability of the latest version online.
Compatibility with breaking changes between versions.
Feasibility of requesting the user to provide the Language Server binary path.
The following open-source plugins make use of LSP:
Prisma ORM (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/prisma/src/org/intellij/prisma/ide/lsp)
Explore third-party plugins using LSP on IntelliJ Platform Explorer (https://jb.gg/ipe?extensions=com.intellij.platform.lsp.serverSupportProvider).
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Plugin projects can target any (custom) IDEs, as long as the products are based on the IntelliJ Platform (The IntelliJ Platform). Such plugins are developed much like plugin projects that target IntelliJ IDEA.
Project configuration attributes common to projects targeting products other than IntelliJ IDEA are described on this page. Details particular to an IntelliJ Platform-based product are described on the individual product pages in Product Specific.
All the Gradle configuration attributes described here are discussed in-depth on the Configuring Gradle IntelliJ Plugin and the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
To create a new Gradle plugin project, follow the tutorial on the Creating a Plugin Gradle Project page. The tutorial produces a skeleton Gradle project suitable to use as a starting point.
Modifications are needed to the skeleton project's Gradle build script and plugin.xml (Plugin Configuration File) files, as described below, and on the individual product pages in Product Specific. The Gradle build script is modified to specify the target product, determining the APIs available during development. The plugin.xml file is modified to declare the plugin's dependencies on modules or libraries.
The best practice is to use the intellij.type ("type" in "Gradle IntelliJ Plugin") property to specify the target product. For example, PY for PyCharm Professional. Configuration using an intellij.type ("type" in "Gradle IntelliJ Plugin") property is explained in the section below.
NOTE: Not all products have an intellij.type ("type" in "Gradle IntelliJ Plugin") property defined by the Gradle IntelliJ Plugin, for example, Android Studio (Android Studio Plugin Development) and PhpStorm (PhpStorm Plugin Development). Please see their respective pages for configuration and below.
To target multiple products (e.g., IntelliJ IDEA and PyCharm) with the same plugin, see Plugin Compatibility with IntelliJ Platform Products page.
If the Gradle IntelliJ Plugin supports a target product directly, there will be an intellij.type ("type" in "Gradle IntelliJ Plugin") property defined. Specifying the target as a product-specific intellij.type ("type" in "Gradle IntelliJ Plugin") property has two advantages:
The APIs available to the plugin will be limited to only what is defined in the target product (unless additional plugin dependencies are specified).
The default IDE Development Instance for running the plugin will be the target product.
A Gradle build script snippet setting a plugin project to target PyCharm (PyCharm Plugin Development) is shown below. The Gradle IntelliJ Plugin will fetch the matching build of PyCharm Professional to define the APIs available, and use that build of PyCharm (and associated JetBrains Runtime) as the Development Instance. No additional product-specific configuration needs to be set in the Gradle build script:
intellij {
type.set("PY")
version.set("2019.2.3")
}intellij {
type = 'PY'
version = '2019.2.3'
}If the Gradle IntelliJ Plugin does not directly support an IntelliJ Platform-based product, the Gradle build script can still be configured to target the desired product. In this case, the build script is configured to use IntelliJ IDEA (Community or Ultimate Edition) as the basis for the available APIs. This does have the drawback that APIs not specific to the target product might accidentally be included in the plugin project. However, testing the plugin project in the target product itself helps to find such mistakes.
Additional configuration must be done to match the version of IntelliJ IDEA to the version of the target product. Understanding the relationship between build numbers is critical when using this approach to project configuration:
targetIDE is the (version-specific) IntelliJ Platform-based IDE in which the plugin is intended to run, such as Android Studio or PhpStorm.
baseIntelliJPlatformVersion is the (version-specific) IntelliJ Platform used in the build of the targetIDE. The IntelliJ Platform is defined by a specific build of the IntelliJ IDEA Community Edition. The Gradle plugin attribute intellij.version ("IntelliJ Platform Configuration" in "Configuring Gradle IntelliJ Plugin") is set to be baseIntelliJPlatformVersion.
For API compatibility, the IntelliJ Platform version used in the targetIDE dictates the baseIntelliJPlatformVersion used for developing a plugin.
The baseIntelliJPlatformVersion used in the targetIDE may not be readily apparent, depending on the product. See the individual product pages in Product Specific for exceptions.
To find the version of the IntelliJ Platform used to build the targetIDE, use the About dialog screen for the targetIDE. Next to Build # is the BRANCH.BUILD.FIX version of the targetIDE. In the example shown below, the BRANCH.BUILD.FIX version is 192.7142.41, and the product version is 2019.2.4. The version of the IntelliJ Platform used to build this product version is BRANCH.BUILD, or 192.7142

If the product version isn't clear on the About screen, consult the individual product pages in Product Specific.
The Other IntelliJ IDEA Versions (https://www.jetbrains.com/idea/download/other.html) page is a way to find build numbers for every product version. Additional ways include hovering over the version number for a product in Toolbox App (https://www.jetbrains.com/toolbox-app/) or examining the About screen for IntelliJ IDEA Community. In this example, IntelliJ IDEA Community Edition (which defines the IntelliJ Platform) for 2019.2.4 is build number 192.7142.36. Although the FIX versions are different, this is not uncommon between products, and the builds are still compatible. The BRANCH and BUILD numbers match, therefore in this PhpStorm example:
The targetIDE is PhpStorm, build 192.7142.41,
The baseIntelliJPlatformVersion (IntelliJ IDEA Community Edition) is build 192.7142.36
This information is used to configure the plugin project's Gradle build script and plugin.xml file.
Configuring a Gradle plugin project for using baseIntelliJPlatformVersion requires changing some default settings in the Gradle build script. Changes need to be made in two places: intellij ("IntelliJ Extension" in "Gradle IntelliJ Plugin") extension and runIde ("runIde" in "Gradle IntelliJ Plugin") task.
The Gradle plugin attributes describing the configuration of the IntelliJ Platform used to build the plugin project ("IntelliJ Platform Configuration" in "Configuring Gradle IntelliJ Plugin") must be explicitly set in the intellij task. The intellij.type ("type" in "Gradle IntelliJ Plugin") is IU because although the IntelliJ IDEA Community Edition defines the IntelliJ Platform, the PHP plugin is only compatible with IntelliJ IDEA Ultimate. The intellij.version ("version" in "Gradle IntelliJ Plugin") is baseIntelliJPlatformVersion.
Any dependencies ("Plugin Dependencies" in "Configuring Gradle IntelliJ Plugin") on targetIDE-specific plugins or modules must be declared in the intellij ("IntelliJ Extension" in "Gradle IntelliJ Plugin") extension. Use the Gradle plugin attribute intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") to declare a dependency. See the specific product pages in Product Specific for the targetIDE plugin or module name.
The best practice is to modify the runIde ("runIde" in "Gradle IntelliJ Plugin") task to use a local installation of targetIDE as the IDE Development Instance. Set the runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") attribute to the (user-specific) absolute path of the targetIDE application. The exact path format varies by operating system.
This snippet is an example for configuring the Setup and Running DSLs in a Gradle build script specific to developing a plugin for targetIDE.
intellij {
// Define IntelliJ Platform against which to build the plugin project.
type.set("IU")
// Use the IntelliJ Platform BRANCH.BUILD version matching
// "targetIDE" (PhpStorm):
version.set("192.7142.36") // baseIntelliJPlatformVersion
// Require the targetIDE plugin or library. Use the stable version
// compatible with intellij.version and intellij.type specified above:
plugins.set(listOf("com.jetbrains.php:192.6603.42"))
}
runIde {
// Absolute path to the installed targetIDE to use as IDE Development
// Instance (the "Contents" directory is macOS specific):
ideDir.set(file("/Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/PhpStorm/ch-0/192.7142.41/PhpStorm.app/Contents"))
}intellij {
// Define IntelliJ Platform against which to build the plugin project.
type = 'IU'
// Use the IntelliJ Platform BRANCH.BUILD version matching
// "targetIDE" (PhpStorm):
version = '192.7142.36' // baseIntelliJPlatformVersion
// Require the targetIDE plugin or library. Use the stable version
// compatible with intellij.version and intellij.type specified above:
plugins = ['com.jetbrains.php:192.6603.42']
}
runIde {
// Absolute path to the installed targetIDE to use as IDE Development
// Instance (the "Contents" directory is macOS specific):
ideDir = file('/Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/PhpStorm/ch-0/192.7142.41/PhpStorm.app/Contents')
}As discussed on the "Declaring Plugin Dependencies" in "Plugin Compatibility with IntelliJ Platform Products" page of this guide, a plugin's dependency on "Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products" must be declared in plugin.xml. When using features (APIs) specific to the target product, a dependency on the target product module must be declared, as shown in the code snippet below. Otherwise, if only general IntelliJ Platform features (APIs) are used, then a dependency on com.intellij.modules.platform must be declared as discussed in Plugin Compatibility with IntelliJ Platform Products.
In the particular case of a plugin project declaring dependencies only on other plugins, it must also declare a dependency on com.intellij.modules.platform. Otherwise, the plugin project is considered to be legacy and will only load in IntelliJ IDEA.
Continuing with the example of developing a plugin for PhpStorm:
<!-- Targeting PhpStorm, so is dependent on the PHP plugin -->
<depends>com.jetbrains.php</depends>
<depends>com.intellij.modules.platform</depends>All products based on the IntelliJ Platform are built on the same underlying API. Some of these products share features built on top of the platform, such as Java support in IntelliJ IDEA and Android Studio. Underlying those shared features are shared components. When authoring a plugin for the IntelliJ Platform, it is important to understand and declare dependencies on these components. Otherwise, it may not be possible to load or run the plugin in a product because the components on which it depends aren't available.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
For the purposes of dependencies, a module can be thought of as a built-in plugin that ships as a non-removable part of a product. A working definition of a dependency is that a plugin project cannot be run without the module present in an IntelliJ Platform-based product. Declaring a dependency on a module also expresses a plugin's compatibility with a product in that the IntelliJ Platform determines whether a product contains the correct modules to support a plugin before loading it.
Plugin Dependencies describes the syntax for declaring plugin dependencies and optional plugin dependencies. This document describes the IntelliJ Platform modules' functionality to aid in determining the dependencies of a plugin.
The way dependency declarations are handled by the IntelliJ Platform is determined by the contents of the plugin.xml (Plugin Configuration File) file:
If a plugin does not declare any dependencies in its plugin.xml file, or if it declares dependencies only on other plugins but not modules, it's assumed to be a legacy plugin and is loaded only in IntelliJ IDEA. This configuration of the dependency declaration is deprecated; do not use it for new plugin projects.
If a plugin declares at least one module dependency in its plugin.xml file, the plugin is loaded if an IntelliJ Platform-based product contains all the modules and plugins on which the plugin has declared a dependency.
Always verify declared dependencies work as expected, see .
A module represents a built-in plugin that is a non-removable part of a product. Some modules are available in all products, and some modules are available only in some, or even just one product. This section identifies and discusses modules of both types.
Starting in 2020.2, a plugin can declare incompatibility with an arbitrary module by specifying <incompatible-with> ("incompatible-with" in "Plugin Configuration File") containing module ID in its plugin.xml.
A core set of modules is available in all standalone IDE products based on the IntelliJ Platform. These modules provide a set of shared functionalities. The following table lists modules that are currently available in all products.
All plugins should declare a dependency on com.intellij.modules.platform to indicate dependence on shared functionality.
Module for <depends> ("depends" in "Plugin Configuration File") Element | Functionality |
|---|---|
com.intellij.modules.platform | Messaging, Themes, UI Components, Files, Documents, Actions, Components, Services, Extensions, Editors |
com.intellij.modules.lang | File Type, Lexer, Parser, Highlighting, References, Code Completion, Find, Rename, Formatter, Code Navigation |
com.intellij.modules.xml | XML, XML DOM, XSD/DTD, DOM Model |
com.intellij.modules.vcs | VCS Revision Numbers, File Status, Change Lists, File History, Annotations |
com.intellij.modules.xdebugger | Debug Session, Stack Frames, Break Points, Source Positions, Memory Views, Tracked Instances |
As of this writing, if a plugin: A) is dependent only on one or more of the modules in the table above, and B) declares those module dependencies in plugin.xml, then any product developed by JetBrains based on the IntelliJ Platform will load it.
More specialized functionality is also delivered via modules and plugins in IntelliJ Platform-based products. For example, the com.intellij.modules.python module supports the Python language-specific functionality. If a plugin uses this module's functionality, such as Python-specific inspections and refactoring, it must declare a dependency on this module.
Note that not all products define and declare modules. For example, PhpStorm does not have its own module, but the product itself depends on (and ships with) the PHP language plugin. A plugin project is compatible with PHP functionality if it declares a dependency on this PHP language plugin.
A high-level feature comparison tool for JetBrains IDEs is available here (https://www.jetbrains.com/products/compare/).
The following table lists modules or built-in plugins that provide specific functionality, and the products currently shipping with them. This table is not exhaustive, to see a list of all modules, invoke code completion inside <depends> element in the plugin.xml file.
Module or Plugin for <depends> ("depends" in "Plugin Configuration File") Element | Functionality | Product Compatibility |
|---|---|---|
com.intellij.modules.java or com.intellij.java See below. | Java language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | IntelliJ IDEA, Android Studio |
com.intellij.modules.androidstudio | Android SDK Platform, Build Tools, Platform Tools, SDK Tools | Android Studio |
com.intellij.modules.cidr.lang | C, C++, Objective-C/C++ language PSI Model, Swift/Objective-C Interaction, Inspections, Intentions, Completion, Refactoring, Test Framework | AppCode, CLion |
com.intellij.modules.cidr.debugger | Debugger Watches, Evaluations, Breakpoints, Inline Debugging | AppCode, CLion, RubyMine |
com.intellij.modules.appcode or com.intellij.appcode See below. | Xcode Project Model, CocoaPods, Core Data Objects, Device & Simulator Support | AppCode |
com.intellij.modules.clion or com.intellij.clion See below. | CMake, Profiler, Embedded Development, Remote Development, Remote Debug, Disassembly | CLion |
com.intellij.cidr.base | Native Debugger Integration, Utility Classes, C/C++ Project Model/Workspace Support (OCWorkspace, CidrWorkspace, etc.), C/C++ Build and Run Support | AppCode, CLion |
com.intellij.database | Database Tools and SQL language PSI Model, Inspections, Completion, Refactoring, Queries | DataGrip, IntelliJ IDEA Ultimate, AppCode, PhpStorm, PyCharm Professional, RubyMine, CLion, GoLand, Rider, and WebStorm if the Database Tools and SQL plugin is installed. |
org.jetbrains.plugins.go | Go language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | GoLand |
com.intellij.modules.python | Python language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | PyCharm, and other products if the Python plugin is installed. |
com.intellij.modules.rider | Connection to ReSharper Process in Background | Rider |
com.intellij.modules.ruby | Ruby language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | RubyMine, and IntelliJ IDEA Ultimate if the Ruby plugin is installed. |
com.intellij.modules.ultimate | Licensing | All commercial IDEs (IntelliJ IDEA Ultimate, PhpStorm, DataGrip, ...) |
com.intellij.swift | Swift language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | AppCode, CLion |
com.jetbrains.php | PHP language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | PhpStorm, and other products if the PHP plugin is installed. |
JavaScript | JavaScript language PSI Model, Inspections, Intentions, Completion, Refactoring, Test Framework | WebStorm, and other products if the JavaScript plugin is installed. |
The Java language functionality (https://blog.jetbrains.com/platform/2019/06/java-functionality-extracted-as-a-plugin/) was extracted as a plugin in version 2019.2 of the IntelliJ Platform. This refactoring separated the Java implementation from the other, non-language portions of the platform. A dependency on the Java plugin (Plugin ID com.intellij.java) must be setup using Plugin Dependencies.
The AppCode and CLion code was restructured (https://blog.jetbrains.com/clion/2020/12/migration-guide-for-plugins-2020-3/) in version 2020.3. This refactoring extracted some functionalities into specific modules for easier maintainability and reuse between AppCode/CLion and other JetBrains IDEs. Consequently, dependencies (Plugin Dependencies) on AppCode (AppCode Plugin Development) and CLion (CLion Plugin Development) functionalities are expressed differently in plugin.xml depending on the version of the IDE being targeted:
Syntax for 2020.3 and later releases:
plugin.xml (allowable alternative):
<depends>com.intellij.clion</depends>Gradle build script (required):
intellij {
plugins.set(listOf("com.intellij.clion"))
}intellij {
plugins = ['com.intellij.clion']
}Syntax required for releases prior to 2020.3, allowable in all releases:
plugin.xml:
<depends>com.intellij.modules.clion</depends>See also: CLion Plugin Development.
Syntax for 2020.3 and later releases:
plugin.xml (allowable alternative):
<depends>com.intellij.appcode</depends>Gradle build script (required):
intellij {
plugins.set(listOf("com.intellij.appcode"))
}intellij {
plugins = ['com.intellij.appcode']
}Syntax required for releases prior to 2020.3, allowable in all releases:
plugin.xml:
<depends>com.intellij.modules.appcode</depends>See also: AppCode Plugin Development.
Once the dependency on a module or plugin (Plugin Dependencies) is declared in plugin.xml, it's useful to explore the packages and classes available in that dependency. The section below gives some recommended procedures for discovering what's available in a module or plugin on which a project depends. These procedures assume a project has the Gradle build script and plugin.xml dependencies configured correctly.
Exploring the available packages and classes in a plugin or module utilizes features in the IntelliJ IDEA IDE.
If the project is not up-to-date, reimport the Gradle project (https://www.jetbrains.com/help/idea/work-with-gradle-projects.html#gradle_refresh_project) as a first step. Reimporting the project will automatically update the dependencies.
In the Project Window, select Project View and scroll to the bottom to see External Libraries (https://www.jetbrains.com/help/idea/project-tool-window.html#content_pane). Look for the library Gradle:unzipped.com.jetbrains.plugins:foo:, where "foo" matches, or is similar to the contents of the <depends> ("depends" in "Plugin Configuration File") tags in plugin.xml or the intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") declaration in the Gradle build script. The image below shows the External Libraries for the example plugin project configuration explained in Configuring Gradle build script ("Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs") and Configuring plugin.xml ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs").

Expand the External Library (as shown) to reveal the JAR files contained in the library. Drill down into the JAR files to expose the packages and (decompiled) classes.
If a project is dependent on a plugin or module, in some cases, the project can also extend (Extensions) the functionality available from the plugin or module.
See Explore the IntelliJ Platform API (Explore the IntelliJ Platform API) for more information and strategies. Dedicated Extension Point Lists specific to IDEs are available under Product Specific.
To browse the opportunities for an extension, start by placing the cursor on the contents of the <depends> ("depends" in "Plugin Configuration File") elements in the project's plugin.xml file. Use the Go to Declaration (https://www.jetbrains.com/help/idea/navigating-through-the-source-code.html#go_to_declaration) IDE feature to navigate to the plugin.xml file for the plugin on which the project depends.
For example, performing this procedure on the <depends>com.jetbrains.php</depends> declaration in a project's plugin.xml file will navigate to the plugin.xml file for the com.jetbrains.php (PHP) project. A common, but not universal, pattern in the IntelliJ Platform is for a plugin (like PHP) to declare <extensionPoints> ("extensionPoints" in "Plugin Configuration File") and then implement each one as <extensions> ("extensions" in "Plugin Configuration File"). Continuing the example, search the PHP plugin's plugin.xml file for:
<extensionPoints> to find the opportunities for extending the PHP plugin's functionality.
<extensions defaultExtensionNs="com.jetbrains.php"> to find where the PHP plugin extends functionality. The extension namespace (in this example com.jetbrains.php) will match the <id> ("id" in "Plugin Configuration File") defined in the plugin.xml file.
If a dependency plugin bundles its API sources (Bundling Plugin API Sources) in the distribution file, the Gradle IntelliJ Plugin (1.7.0+) will attach them to the plugin dependency in IDE, making sources available when navigating to the plugin API classes.
Before marking a plugin project as dependent only on modules in a target product in addition to com.intellij.modules.platform, verify the plugin isn't implicitly dependent on any APIs that are specific to IntelliJ IDEA.
For Gradle-based (Developing a Plugin) projects, "Plugin Verifier" in "Verifying Plugin Compatibility" can be used to ensure compatibility with all specified target IDEs.
For DevKit-based projects, create an SDK ("Add IntelliJ Platform Plugin SDK" in "Setting Up a Development Environment") pointing to an installation of the intended target IntelliJ Platform-based product, e.g., PhpStorm, rather than IntelliJ IDEA. Use the same development version of the IntelliJ Platform as the targeted product.
Based on the tables above, the JetBrains Marketplace (https://plugins.jetbrains.com/) automatically detects the JetBrains products with which a plugin is compatible, and makes the compatibility information available to plugin authors. The compatibility information determines if plugins are available for users of a particular JetBrains product.
The API of IntelliJ Platform and bundled plugins may change between releases. The significant changes that may break plugins are listed on Incompatible Changes in IntelliJ Platform and Plugins API (Incompatible Changes in IntelliJ Platform and Plugins API) page.
IDE: Homepage (https://developer.android.com/studio), Versions
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/androidstudio)
Android Studio plugins extend or add functionality to the Android Studio IDE (https://developer.android.com/studio). Plugins can be written in Kotlin (Configuring Kotlin Support) or Java, or a mix of both, and are created using IntelliJ IDEA and the IntelliJ Platform (The IntelliJ Platform). It's also helpful to be familiar with Java Swing (https://docs.oracle.com/javase/8/javase-clienttechnologies.htm). Once completed, plugins can be packaged and distributed at JetBrains Marketplace (https://plugins.jetbrains.com).
Android Studio plugins are not Android modules or apps to run in the Android operating system, such as smartphones or tablets.
To create a new Android Studio plugin project, follow the tutorial on the Getting Started with Gradle (Creating a Plugin Gradle Project) page. The tutorial produces a skeleton project suitable to use as a starting point for an Android Studio plugin. On the New Project Screen ("Create IDE Plugin" in "Creating a Plugin Gradle Project"), choose IDE Plugin from the project generators list as described in the tutorial, not Android. Some minor modifications to the skeleton project are needed, as discussed below.
For API compatibility, it is essential to match the version of the IntelliJ Platform APIs used for plugin development with the target version of Android Studio. The version number of Android Studio contains the version of the underlying IntelliJ Platform APIs that were used to build it.
The actual Android Studio version doesn't entirely reflect the (YEAR.MAJOR.MINOR) version of the IntelliJ Platform. The Android Studio version presented below is 2021.1.1 Patch 1, but the 2021.1 part marked with the green rectangle refers to the IntelliJ IDEA release.
To find the version of the IntelliJ Platform used to build Android Studio, use the Android Studio About dialog screen. An example is shown below. In this case, the (BRANCH.BUILD.FIX) version of the IntelliJ Platform is 211.7628.21 – marked with the blue rectangle – is corresponding to the IntelliJ IDEA version 2021.1.3.
In your Gradle build script, you should set both versions – build number and the release number – to the intellij.version ("version" in "Gradle IntelliJ Plugin") property. To figure out the exact release number based on the build number, visit the IntelliJ Repository Releases (https://www.jetbrains.com/intellij-repository/releases/) listing and check the com.jetbrains.intellij.idea section.
The Gradle build script configuration steps section below explains how to set the IntelliJ Platform version to match the target version of Android Studio.

Below, you may find a list of recent Android Studio releases mapped to the relevant IntelliJ IDEA versions:
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Koala \| 2024.1.1 Canary 3 | April 2, 2024 | 2024.1.1.1 AI-241.14494.158.2411.11648550 | 2024.1 241.14494.158 | |
Koala \| 2023.3.2 Canary 2 | March 22, 2024 | 2023.3.2.2 AI-233.14475.28.2332.11606850 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 RC 1 | April 4, 2024 | 2023.3.1.16 AI-233.14808.21.2331.11643467 | 2023.3.5 233.14808.21 | |
Jellyfish \| 2023.3.1 Beta 2 | March 27, 2024 | 2023.3.1.15 AI-233.14808.21.2331.11608968 | 2023.3.5 233.14808.21 | |
Jellyfish \| 2023.3.1 Beta 1 | March 21, 2024 | 2023.3.1.14 AI-233.14808.21.2331.11574862 | 2023.3.5 233.14808.21 |
For the full list of Android Studio releases with more details, visit the Android Studio Releases List (Android Studio Releases List) page.
The use-case of developing for a non-IntelliJ IDEA IDE is reviewed in the Plugins Targeting Alternate IntelliJ Platform-Based IDEs ("Configuring Gradle Build Script to Target Products Other Than IntelliJ IDEA" in "Plugins Targeting IntelliJ Platform-Based IDEs") section. The particular example in that section discusses configuring a plugin project for PhpStorm, so the details for an Android Studio plugin project are reviewed here.
Here are the steps to configure the Gradle build script for developing a plugin to target Android Studio:
The Gradle plugin attributes describing the configuration of the IntelliJ Platform used to build the plugin project ("IntelliJ Platform Configuration" in "Configuring Gradle IntelliJ Plugin") must be explicitly set. Continuing with the example above, set the intellij.version ("version" in "Gradle IntelliJ Plugin") value to 191.8026.42. Alternatively, specify intellij.localPath ("localPath" in "Gradle IntelliJ Plugin") to refer to a local installation of Android Studio.
Android Studio plugin projects that use APIs from the android plugin must declare a dependency on that plugin. Declare the dependency in the Gradle build script using the intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") attribute, which in this case lists the directory name ("pluginName" in "Gradle IntelliJ Plugin") of the plugin.
The best practice is to use the target version of Android Studio as the IDE Development Instance. Set the Development Instance to the (user-specific) absolute path to the target Android Studio application.
The snippet below is an example of configuring the Setup and Running DSLs in a Gradle build script specific to developing a plugin targeted at Android Studio.
intellij {
// Define IntelliJ Platform against which to build the plugin project.
// Same IntelliJ IDEA version (2019.1.4) as target 3.5 Android Studio:
version.set("191.8026.42")
// Use IntelliJ IDEA CE because it's the basis of the IntelliJ Platform:
type.set("IC")
// Require the Android plugin (Gradle will choose the correct version):
plugins.set(listOf("android"))
}
tasks {
runIde {
// Absolute path to installed target 3.5 Android Studio to use as
// IDE Development Instance (the "Contents" directory is macOS specific):
ideDir.set(file("/Applications/Android Studio.app/Contents"))
}
}intellij {
// Define IntelliJ Platform against which to build the plugin project.
// Same IntelliJ IDEA version (2019.1.4) as target 3.5 Android Studio:
version = '191.8026.42'
// Use IntelliJ IDEA CE because it's the basis of the IntelliJ Platform:
type = 'IC'
// Require the Android plugin (Gradle will choose the correct version):
plugins = ['android']
}
runIde {
// Absolute path to installed target 3.5 Android Studio to use as
// IDE Development Instance (the "Contents" directory is macOS specific):
ideDir = file('/Applications/Android Studio.app/Contents')
}When using APIs from the android plugin, declare a dependency:
<depends>org.jetbrains.android</depends>As discussed in the Plugin Dependencies ("Declaring Plugin Dependencies" in "Plugin Compatibility with IntelliJ Platform Products") section of this guide, a plugin's dependency on Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") must be declared in plugin.xml (Plugin Configuration File). When using Android Studio-specific features (APIs), a dependency on com.intellij.modules.androidstudio must be declared as shown in the code snippet below. Otherwise, if only general IntelliJ Platform features (APIs) are used, then a dependency on com.intellij.modules.platform must be declared as discussed in Plugin Compatibility with IntelliJ Platform Products (Plugin Compatibility with IntelliJ Platform Products).
<depends>com.intellij.modules.androidstudio</depends>Discussion of extending Android Lint - How to Register AndroidLintInspectionBase in IntelliJIdea Plugin (https://intellij-support.jetbrains.com/hc/en-us/community/posts/360005018559-How-to-register-AndroidLintInspectionBase-in-IntellijIdea-Plugin)
Grzegorz Matyszczak's article How I Automated Creating Files for a New Screen with My Own Android Studio Plugin (https://proandroiddev.com/how-i-automated-creating-files-for-a-new-screen-with-my-own-android-studio-plugin-5d54b14ba6fa)
Marcos Holgado's article series Write an Android Studio Plugin (Part 1) (https://proandroiddev.com/write-an-android-studio-plugin-part-1-creating-a-basic-plugin-af956c4f8b50)
When learning new development configurations, it is helpful to have some representative projects for reference:
ADB Idea (https://github.com/pbreault/adb-idea) plugin for Android Studio and IntelliJ IDEA that speeds up Android development.
Android postfix plugin (https://github.com/takahirom/android-postfix-plugin) for Android Studio.
Flutter Plugin (https://github.com/flutter/flutter-intellij).
Bal Sikandar's list of Android Studio plugins (https://github.com/balsikandar/Android-Studio-Plugins).
Use GradleSyncInvoker.requestProjectSync() (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system-gradle/src/com/android/tools/idea/gradle/project/sync/GradleSyncInvoker.kt) for programmatic synchronization.
IntelliJ Android Plugin README (https://github.com/JetBrains/android#contents)
This page contains a complete list of the Android Studio releases with the relevant IntelliJ IDEA release version specified.
Android Studio releases are grouped by major versions. Each entry specifies the exact release channel (like Canary, Beta, RC, Release, Patch), release date, and exact IDE version with build number for the actual IDE and the IntelliJ IDEA platform that Android Studio is based on.
This data is updated daily and also available in JSON (https://jb.gg/android-studio-releases-list.json) and XML (https://jb.gg/android-studio-releases-list.xml) formats.
To get an insight on how to develop a plugin for Android Studio, see Android Studio Plugin Development.
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Koala \| 2023.3.2 Canary 2 | March 22, 2024 | 2023.3.2.2 AI-233.14475.28.2332.11606850 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.2 Canary 1 | March 19, 2024 | 2023.3.2.1 AI-233.14475.28.2332.11578541 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 RC 1 | April 4, 2024 | 2023.3.1.16 AI-233.14808.21.2331.11643467 | 2023.3.5 233.14808.21 | |
Jellyfish \| 2023.3.1 Beta 2 | March 27, 2024 | 2023.3.1.15 AI-233.14808.21.2331.11608968 | 2023.3.5 233.14808.21 | |
Jellyfish \| 2023.3.1 Beta 1 | March 21, 2024 | 2023.3.1.14 AI-233.14808.21.2331.11574862 | 2023.3.5 233.14808.21 | |
Jellyfish \| 2023.3.1 Canary 13 | March 7, 2024 | 2023.3.1.13 AI-233.14475.28.2331.11543046 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 Canary 12 | February 29, 2024 | 2023.3.1.12 AI-233.14475.28.2331.11514062 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 Canary 11 | February 22, 2024 | 2023.3.1.11 AI-233.14475.28.2331.11483111 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 Canary 10 | February 16, 2024 | 2023.3.1.10 AI-233.14475.28.2331.11456607 | 2023.3.4 233.14475.28 | |
Jellyfish \| 2023.3.1 Canary 9 | February 8, 2024 | 2023.3.1.9 AI-233.14015.106.2331.11423272 | 2023.3.3 233.14015.106 | |
Jellyfish \| 2023.3.1 Canary 8 | February 5, 2024 | 2023.3.1.8 AI-233.14015.106.2331.11392117 | 2023.3.3 233.14015.106 | |
Jellyfish \| 2023.3.1 Canary 7 | January 26, 2024 | 2023.3.1.7 AI-233.13135.103.2331.11360849 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 6 | January 24, 2024 | 2023.3.1.6 AI-233.13135.103.2331.11350331 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 5 | January 22, 2024 | 2023.3.1.5 AI-233.13135.103.2331.11329247 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 4 | January 12, 2024 | 2023.3.1.4 AI-233.13135.103.2331.11301475 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 3 | January 4, 2024 | 2023.3.1.3 AI-233.13135.103.2331.11273936 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 2 | January 2, 2024 | 2023.3.1.2 AI-233.13135.103.2331.11259770 | 2023.3.2 233.13135.103 | |
Jellyfish \| 2023.3.1 Canary 1 | December 28, 2023 | 2023.3.1.1 AI-233.13135.103.2331.11247450 | 2023.3.2 233.13135.103 | |
Iguana \| 2023.2.1 Patch 1 | March 18, 2024 | 2023.2.1.24 AI-232.10300.40.2321.11567975 | 2023.2.6 232.10300.40 | |
Iguana \| 2023.2.1 | February 29, 2024 | 2023.2.1.23 AI-232.10227.8.2321.11479570 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 RC 2 | February 13, 2024 | 2023.2.1.22 AI-232.10227.8.2321.11429013 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 RC 1 | February 5, 2024 | 2023.2.1.21 AI-232.10227.8.2321.11379558 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Beta 2 | January 10, 2024 | 2023.2.1.20 AI-232.10227.8.2321.11280706 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Beta 1 | December 26, 2023 | 2023.2.1.19 AI-232.10227.8.2321.11231672 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 18 | December 13, 2023 | 2023.2.1.18 AI-232.10227.8.2321.11203637 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 17 | December 7, 2023 | 2023.2.1.17 AI-232.10227.8.2321.11191411 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 16 | November 30, 2023 | 2023.2.1.16 AI-232.10227.8.2321.11161997 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 15 | November 28, 2023 | 2023.2.1.15 AI-232.10227.8.2321.11145877 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 14 | November 16, 2023 | 2023.2.1.14 AI-232.10227.8.2321.11110254 | 2023.2.5 232.10227.8 | |
Iguana \| 2023.2.1 Canary 13 | November 9, 2023 | 2023.2.1.13 AI-232.10203.10.2321.11075088 | 2023.2.4 232.10203.10 | |
Iguana \| 2023.2.1 Canary 12 | November 2, 2023 | 2023.2.1.12 AI-232.10203.10.2321.11040444 | 2023.2.4 232.10203.10 | |
Iguana \| 2023.2.1 Canary 11 | October 26, 2023 | 2023.2.1.11 AI-232.10072.27.2321.11006994 | 2023.2.3 232.10072.27 | |
Iguana \| 2023.2.1 Canary 10 | October 23, 2023 | 2023.2.1.10 AI-232.10072.27.2321.10984096 | 2023.2.3 232.10072.27 | |
Iguana \| 2023.2.1 Canary 9 | October 19, 2023 | 2023.2.1.9 AI-232.10072.27.2321.10977308 | 2023.2.3 232.10072.27 | |
Iguana \| 2023.2.1 Canary 8 | October 19, 2023 | 2023.2.1.8 AI-232.9921.47.2321.10958940 | 2023.2.2 232.9921.47 | |
Iguana \| 2023.2.1 Canary 7 | October 5, 2023 | 2023.2.1.7 AI-232.9921.47.2321.10905494 | 2023.2.2 232.9921.47 | |
Iguana \| 2023.2.1 Canary 6 | September 28, 2023 | 2023.2.1.6 AI-232.9921.47.2321.10875067 | 2023.2.2 232.9921.47 | |
Iguana \| 2023.2.1 Canary 5 | September 21, 2023 | 2023.2.1.5 AI-232.9921.47.2321.10840167 | 2023.2.2 232.9921.47 | |
Iguana \| 2023.2.1 Canary 4 | September 15, 2023 | 2023.2.1.4 AI-232.9921.47.2321.10809744 | 2023.2.2 232.9921.47 | |
Iguana \| 2023.2.1 Canary 3 | September 7, 2023 | 2023.2.1.3 AI-232.9559.62.2321.10773421 | 2023.2.1 232.9559.62 | |
Iguana \| 2023.2.1 Canary 2 | September 5, 2023 | 2023.2.1.2 AI-232.9559.62.2321.10749089 | 2023.2.1 232.9559.62 | |
Iguana \| 2023.2.1 Canary 1 | August 25, 2023 | 2023.2.1.1 AI-232.8660.185.2321.10696284 | 2023.2 232.8660.185 | |
Hedgehog \| 2023.1.1 Patch 2 | January 23, 2024 | 2023.1.1.28 AI-231.9392.1.2311.11330709 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Patch 1 | January 3, 2024 | 2023.1.1.27 AI-231.9392.1.2311.11255304 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 | November 30, 2023 | 2023.1.1.26 AI-231.9392.1.2311.11076708 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 RC 3 | November 9, 2023 | 2023.1.1.25 AI-231.9392.1.2311.11047128 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 RC 2 | October 30, 2023 | 2023.1.1.24 AI-231.9392.1.2311.10980684 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 RC 1 | October 17, 2023 | 2023.1.1.23 AI-231.9392.1.2311.10949950 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 6 | September 27, 2023 | 2023.1.1.22 AI-231.9392.1.2311.10844163 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 5 | September 21, 2023 | 2023.1.1.21 AI-231.9392.1.2311.10809438 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 4 | September 13, 2023 | 2023.1.1.20 AI-231.9392.1.2311.10779794 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 3 | September 7, 2023 | 2023.1.1.19 AI-231.9392.1.2311.10749307 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 2 | September 5, 2023 | 2023.1.1.18 AI-231.9392.1.2311.10717887 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Beta 1 | August 24, 2023 | 2023.1.1.17 AI-231.9392.1.2311.10680768 | 2023.1.5 231.9392.1 | |
Hedgehog \| 2023.1.1 Canary 16 | August 14, 2023 | 2023.1.1.16 AI-231.9225.16.2311.10647019 | 2023.1.4 231.9225.16 | |
Hedgehog \| 2023.1.1 Canary 15 | July 31, 2023 | 2023.1.1.15 AI-231.9225.16.2311.10572941 | 2023.1.4 231.9225.16 | |
Hedgehog \| 2023.1.1 Canary 14 | July 25, 2023 | 2023.1.1.14 AI-231.9225.16.2311.10533651 | 2023.1.4 231.9225.16 | |
Hedgehog \| 2023.1.1 Canary 13 | July 18, 2023 | 2023.1.1.13 AI-231.9161.38.2311.10491752 | 2023.1.3 231.9161.38 | |
Hedgehog \| 2023.1.1 Canary 12 | July 11, 2023 | 2023.1.1.12 AI-231.9161.38.2311.10457801 | 2023.1.3 231.9161.38 | |
Hedgehog \| 2023.1.1 Canary 11 | July 6, 2023 | 2023.1.1.11 AI-231.9161.38.2311.10413018 | 2023.1.3 231.9161.38 | |
Hedgehog \| 2023.1.1 Canary 10 | June 27, 2023 | 2023.1.1.10 AI-231.9011.34.2311.10366083 | 2023.1.2 231.9011.34 | |
Hedgehog \| 2023.1.1 Canary 9 | June 20, 2023 | 2023.1.1.9 AI-231.9011.34.2311.10335024 | 2023.1.2 231.9011.34 | |
Hedgehog \| 2023.1.1 Canary 8 | June 13, 2023 | 2023.1.1.8 AI-231.9011.34.2311.10290408 | 2023.1.2 231.9011.34 | |
Hedgehog \| 2023.1.1 Canary 7 | June 6, 2023 | 2023.1.1.7 AI-231.9011.34.2311.10238683 | 2023.1.2 231.9011.34 | |
Hedgehog \| 2023.1.1 Canary 6 | May 30, 2023 | 2023.1.1.6 AI-231.8109.175.2311.10205233 | 2023.1 231.8109.175 | |
Hedgehog \| 2023.1.1 Canary 5 | May 25, 2023 | 2023.1.1.5 AI-231.7864.76.2311.10195651 | 2023.1 231.7864.76 | |
Hedgehog \| 2023.1.1 Canary 4 | May 16, 2023 | 2023.1.1.4 AI-231.7864.76.2311.10114981 | 2023.1 231.7864.76 | |
Hedgehog \| 2023.1.1 Canary 3 | May 10, 2023 | 2023.1.1.3 AI-231.7864.76.2311.10101468 | 2023.1 231.7864.76 | |
Hedgehog \| 2023.1.1 Canary 2 | May 2, 2023 | 2023.1.1.2 AI-231.6890.12.2311.10027519 | 2023.1 231.6890.12 | |
Hedgehog \| 2023.1.1 Canary 1 | April 25, 2023 | 2023.1.1.1 AI-223.8836.35.2311.9976484 | 2022.3.3 223.8836.35 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Koala \| 2024.1.1 Canary 3 | April 2, 2024 | 2024.1.1.1 AI-241.14494.158.2411.11648550 | 2024.1 241.14494.158 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Giraffe \| 2022.3.1 Patch 4 | November 16, 2023 | 2022.3.1.22 AI-223.8836.35.2231.11090377 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Patch 3 | November 7, 2023 | 2022.3.1.21 AI-223.8836.35.2231.11005911 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Patch 2 | September 28, 2023 | 2022.3.1.20 AI-223.8836.35.2231.10811636 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Patch 1 | August 21, 2023 | 2022.3.1.19 AI-223.8836.35.2231.10671973 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 | July 25, 2023 | 2022.3.1.18 AI-223.8836.35.2231.10406996 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 RC 1 | June 22, 2023 | 2022.3.1.17 AI-223.8836.35.2231.10320515 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Beta 5 | June 8, 2023 | 2022.3.1.16 AI-223.8836.35.2231.10271316 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Beta 4 | May 30, 2023 | 2022.3.1.15 AI-223.8836.35.2231.10190587 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Beta 3 | May 16, 2023 | 2022.3.1.14 AI-223.8836.35.2231.10075884 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Beta 2 | May 8, 2023 | 2022.3.1.13 AI-223.8836.35.2231.10023527 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Beta 1 | April 20, 2023 | 2022.3.1.12 AI-223.8836.35.2231.9923731 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Canary 11 | March 30, 2023 | 2022.3.1.11 AI-223.8836.35.2231.9848316 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Canary 10 | March 21, 2023 | 2022.3.1.10 AI-223.8836.35.2231.9762515 | 2022.3.3 223.8836.35 | |
Giraffe \| 2022.3.1 Canary 9 | March 14, 2023 | 2022.3.1.9 AI-223.8617.56.2231.9716135 | 2022.3.2 223.8617.56 | |
Giraffe \| 2022.3.1 Canary 8 | March 6, 2023 | 2022.3.1.8 AI-223.8617.56.2231.9687552 | 2022.3.2 223.8617.56 | |
Giraffe \| 2022.3.1 Canary 7 | February 27, 2023 | 2022.3.1.7 AI-223.8617.56.2231.9644228 | 2022.3.2 223.8617.56 | |
Giraffe \| 2022.3.1 Canary 6 | February 21, 2023 | 2022.3.1.6 AI-223.8214.52.2231.9615888 | 2022.3.1 223.8214.52 | |
Giraffe \| 2022.3.1 Canary 5 | February 16, 2023 | 2022.3.1.5 AI-223.8214.52.2231.9601041 | 2022.3.1 223.8214.52 | |
Giraffe \| 2022.3.1 Canary 4 | February 9, 2023 | 2022.3.1.4 AI-223.7571.182.2231.9569140 | 2022.3 223.7571.182 | |
Giraffe \| 2022.3.1 Canary 3 | February 7, 2023 | 2022.3.1.3 AI-223.7571.182.2231.9532861 | 2022.3 223.7571.182 | |
Giraffe \| 2022.3.1 Canary 2 | January 26, 2023 | 2022.3.1.2 AI-223.7571.182.2231.9523943 | 2022.3 223.7571.182 | |
Giraffe \| 2022.3.1 Canary 1 | January 17, 2023 | 2022.3.1.1 AI-223.4884.69.2231.9486165 | 2022.3 223.4884.69 | |
Flamingo \| 2022.2.1 Patch 2 | May 24, 2023 | 2022.2.1.20 AI-222.4459.24.2221.10121639 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Patch 1 | May 1, 2023 | 2022.2.1.19 AI-222.4459.24.2221.9971841 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 | April 13, 2023 | 2022.2.1.18 AI-222.4459.24.2221.9862592 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 RC 1 | March 27, 2023 | 2022.2.1.17 AI-222.4459.24.2221.9787799 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Beta 5 | March 9, 2023 | 2022.2.1.16 AI-222.4459.24.2221.9682058 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Beta 4 | March 2, 2023 | 2022.2.1.15 AI-222.4459.24.2221.9645777 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Beta 3 | February 16, 2023 | 2022.2.1.14 AI-222.4459.24.2221.9601061 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Beta 2 | February 7, 2023 | 2022.2.1.13 AI-222.4459.24.2221.9526055 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Beta 1 | January 17, 2023 | 2022.2.1.12 AI-222.4459.24.2221.9471578 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Canary 11 | January 4, 2023 | 2022.2.1.11 AI-222.4459.24.2221.9445173 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Canary 10 | December 21, 2022 | 2022.2.1.10 AI-222.4459.24.2221.9409768 | 2022.2.4 222.4459.24 | |
Flamingo \| 2022.2.1 Canary 9 | November 30, 2022 | 2022.2.1.9 AI-222.4345.14.2221.9321504 | 2022.2.3 222.4345.14 | |
Flamingo \| 2022.2.1 Canary 8 | November 7, 2022 | 2022.2.1.8 AI-222.4345.14.2221.9252092 | 2022.2.3 222.4345.14 | |
Flamingo \| 2022.2.1 Canary 7 | November 2, 2022 | 2022.2.1.7 AI-222.4345.14.2221.9228443 | 2022.2.3 222.4345.14 | |
Flamingo \| 2022.2.1 Canary 6 | October 24, 2022 | 2022.2.1.6 AI-222.4345.14.2221.9189903 | 2022.2.3 222.4345.14 | |
Flamingo \| 2022.2.1 Canary 5 | October 18, 2022 | 2022.2.1.5 AI-222.4345.14.2221.9178080 | 2022.2.3 222.4345.14 | |
Flamingo \| 2022.2.1 Canary 4 | October 14, 2022 | 2022.2.1.4 AI-222.4167.29.2221.9153536 | 2022.2.2 222.4167.29 | |
Flamingo \| 2022.2.1 Canary 3 | October 6, 2022 | 2022.2.1.3 AI-222.4167.29.2221.9133400 | 2022.2.2 222.4167.29 | |
Flamingo \| 2022.2.1 Canary 2 | September 27, 2022 | 2022.2.1.2 AI-222.4167.29.2221.9093980 | 2022.2.2 222.4167.29 | |
Flamingo \| 2022.2.1 Canary 1 | September 20, 2022 | 2022.2.1.1 AI-222.3739.54.2221.9043926 | 2022.2.1 222.3739.54 | |
Electric Eel \| 2022.1.1 Patch 2 | February 27, 2023 | 2022.1.1.21 AI-221.6008.13.2211.9619390 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Patch 1 | January 31, 2023 | 2022.1.1.20 AI-221.6008.13.2211.9514443 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 | January 12, 2023 | 2022.1.1.19 AI-221.6008.13.2211.9477386 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 RC 3 | December 27, 2022 | 2022.1.1.18 AI-221.6008.13.2211.9424903 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 RC 2 | December 21, 2022 | 2022.1.1.17 AI-221.6008.13.2211.9392097 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 RC 1 | November 30, 2022 | 2022.1.1.16 AI-221.6008.13.2211.9301383 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Beta 5 | November 7, 2022 | 2022.1.1.15 AI-221.6008.13.2211.9237616 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Beta 4 | October 28, 2022 | 2022.1.1.14 AI-221.6008.13.2211.9208527 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Beta 3 | October 24, 2022 | 2022.1.1.13 AI-221.6008.13.2211.9173235 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Beta 2 | October 3, 2022 | 2022.1.1.12 AI-221.6008.13.2211.9113387 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Beta 1 | September 20, 2022 | 2022.1.1.11 AI-221.6008.13.2211.9039819 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Canary 10 | August 26, 2022 | 2022.1.1.10 AI-221.6008.13.2211.8963757 | 2022.1.4 221.6008.13 | |
Electric Eel \| 2022.1.1 Canary 9 | August 3, 2022 | 2022.1.1.9 AI-221.5921.22.2211.8881706 | 2022.1.3 221.5921.22 | |
Electric Eel \| 2022.1.1 Canary 8 | July 6, 2022 | 2022.1.1.8 AI-221.5921.22.2211.8786657 | 2022.1.3 221.5921.22 | |
Electric Eel \| 2022.1.1 Canary 7 | June 29, 2022 | 2022.1.1.7 AI-221.5591.52.2211.8767654 | 2022.1.1 221.5591.52 | |
Electric Eel \| 2022.1.1 Canary 6 | June 27, 2022 | 2022.1.1.6 AI-221.4501.155.2211.8738788 | 2022.1 221.4501.155 | |
Electric Eel \| 2022.1.1 Canary 5 | June 16, 2022 | 2022.1.1.5 AI-221.4165.146.2211.8713749 | 2022.1 221.4165.146 | |
Electric Eel \| 2022.1.1 Canary 4 | June 13, 2022 | 2022.1.1.4 AI-221.3427.89.2211.8689873 | 2022.1 221.3427.89 | |
Electric Eel \| 2022.1.1 Canary 3 | June 3, 2022 | 2022.1.1.3 AI-213.7172.25.2211.8624637 | 2021.3.3 213.7172.25 | |
Electric Eel \| 2022.1.1 Canary 2 | May 13, 2022 | 2022.1.1.2 AI-213.7172.25.2211.8571212 | 2021.3.3 213.7172.25 | |
Electric Eel \| 2022.1.1 Canary 1 | May 11, 2022 | 2022.1.1.1 AI-213.7172.25.2211.8563311 | 2021.3.3 213.7172.25 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Dolphin \| 2021.3.1 Patch 1 | October 13, 2022 | 2021.3.1.17 AI-213.7172.25.2113.9123335 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 | September 15, 2022 | 2021.3.1.16 AI-213.7172.25.2113.9014738 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 RC 1 | August 16, 2022 | 2021.3.1.15 AI-213.7172.25.2113.8913347 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Beta 5 | July 7, 2022 | 2021.3.1.14 AI-213.7172.25.2113.8774922 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Beta 4 | June 22, 2022 | 2021.3.1.13 AI-213.7172.25.2113.8718688 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Beta 3 | June 13, 2022 | 2021.3.1.12 AI-213.7172.25.2113.8684940 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Beta 1 | May 11, 2022 | 2021.3.1.10 AI-213.7172.25.2113.8565817 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Canary 9 | April 21, 2022 | 2021.3.1.9 AI-213.7172.25.2113.8473230 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Canary 8 | April 11, 2022 | 2021.3.1.8 AI-213.7172.25.2113.8421465 | 2021.3.3 213.7172.25 | |
Dolphin \| 2021.3.1 Canary 7 | March 17, 2022 | 2021.3.1.7 AI-213.6777.52.2113.8305692 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 6 | March 14, 2022 | 2021.3.1.6 AI-213.6777.52.2113.8265607 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 5 | March 3, 2022 | 2021.3.1.5 AI-213.6777.52.2113.8233036 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 4 | February 28, 2022 | 2021.3.1.4 AI-213.6777.52.2113.8195552 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 3 | February 14, 2022 | 2021.3.1.3 AI-213.6777.52.2113.8172706 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 2 | February 9, 2022 | 2021.3.1.2 AI-213.6777.52.2113.8156070 | 2021.3.2 213.6777.52 | |
Dolphin \| 2021.3.1 Canary 1 | January 31, 2022 | 2021.3.1.1 AI-213.5744.223.2113.8103819 | 2021.3 213.5744.223 | |
Chipmunk \| 2021.2.1 Patch 2 | August 3, 2022 | 2021.2.1.16 AI-212.5712.43.2112.8815526 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 Patch 1 | May 25, 2022 | 2021.2.1.15 AI-212.5712.43.2112.8609683 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 | May 9, 2022 | 2021.2.1.14 AI-212.5712.43.2112.8512546 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 RC 2 | May 2, 2022 | 2021.2.1.13 AI-212.5712.43.2112.8504664 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 RC 1 | April 18, 2022 | 2021.2.1.12 AI-212.5712.43.2112.8403254 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 Beta 4 | March 9, 2022 | 2021.2.1.11 AI-212.5712.43.2112.8233820 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 Beta 3 | February 28, 2022 | 2021.2.1.10 AI-212.5712.43.2112.8184640 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 Beta 2 | February 9, 2022 | 2021.2.1.9 AI-212.5712.43.2112.8125332 | 2021.2.4 212.5712.43 | |
Chipmunk \| 2021.2.1 Beta 1 | January 25, 2022 | 2021.2.1.8 AI-212.5457.46.2112.8094850 | 2021.2.3 212.5457.46 | |
Chipmunk \| 2021.2.1 Canary 7 | January 10, 2022 | 2021.2.1.7 AI-212.5457.46.2112.8043657 | 2021.2.3 212.5457.46 | |
Chipmunk \| 2021.2.1 Canary 6 | December 15, 2021 | 2021.2.1.6 AI-212.5457.46.2112.7968471 | 2021.2.3 212.5457.46 | |
Chipmunk \| 2021.2.1 Canary 5 | November 15, 2021 | 2021.2.1.5 AI-212.5457.46.2112.7905983 | 2021.2.3 212.5457.46 | |
Chipmunk \| 2021.2.1 Canary 4 | November 9, 2021 | 2021.2.1.4 AI-212.5284.40.2112.7863073 | 2021.2.2 212.5284.40 | |
Chipmunk \| 2021.2.1 Canary 3 | October 27, 2021 | 2021.2.1.3 AI-212.5284.40.2112.7855545 | 2021.2.2 212.5284.40 | |
Chipmunk \| 2021.2.1 Canary 2 | October 19, 2021 | 2021.2.1.2 AI-212.5284.40.2112.7824075 | 2021.2.2 212.5284.40 | |
Chipmunk \| 2021.2.1 Canary 1 | October 13, 2021 | 2021.2.1.1 AI-212.4037.9.2112.7818732 | 2021.2 212.4037.9 | |
Bumblebee \| 2021.1.1 Patch 3 | April 7, 2022 | 2021.1.1.23 AI-211.7628.21.2111.8309675 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Patch 2 | February 23, 2022 | 2021.1.1.22 AI-211.7628.21.2111.8193401 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Patch 1 | February 04, 2022 | 2021.1.1.21 AI-211.7628.21.2111.8139111 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 | January 25, 2022 | 2021.1.1.20 AI-211.7628.21.2111.8092744 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 RC 1 | January 4, 2022 | 2021.1.1.19 AI-211.7628.21.2111.8005941 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Beta 5 | December 13, 2021 | 2021.1.1.18 AI-211.7628.21.2111.7956428 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Beta 4 | November 22, 2021 | 2021.1.1.17 AI-211.7628.21.2111.7905991 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Beta 3 | November 9, 2021 | 2021.1.1.16 AI-211.7628.21.2111.7863044 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Beta 2 | October 28, 2021 | 2021.1.1.15 AI-211.7628.21.2111.7824002 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Beta 1 | October 13, 2021 | 2021.1.1.14 AI-211.7628.21.2111.7785763 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 13 | September 29, 2021 | 2021.1.1.13 AI-211.7628.21.2111.7762732 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 12 | September 17, 2021 | 2021.1.1.12 AI-211.7628.21.2111.7699744 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 11 | September 2, 2021 | 2021.1.1.11 AI-211.7628.21.2111.7676841 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 10 | August 25, 2021 | 2021.1.1.10 AI-211.7628.21.2111.7667256 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 9 | August 23, 2021 | 2021.1.1.9 AI-211.7628.21.2111.7653804 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 8 | August 11, 2021 | 2021.1.1.8 AI-211.7628.21.2111.7619903 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 7 | August 9, 2021 | 2021.1.1.7 AI-211.7628.21.2111.7615328 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 6 | July 29, 2021 | 2021.1.1.6 AI-211.7628.21.2111.7584175 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 5 | July 27, 2021 | 2021.1.1.5 AI-211.7628.21.2111.7579519 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 4 | July 21, 2021 | 2021.1.1.4 AI-211.7628.21.2111.7545178 | 2021.1.3 211.7628.21 | |
Bumblebee \| 2021.1.1 Canary 3 | July 8, 2021 | 2021.1.1.3 AI-211.7442.40.2111.7518594 | 2021.1.2 211.7442.40 | |
Bumblebee \| 2021.1.1 Canary 2 | June 7, 2021 | 2021.1.1.2 AI-211.6222.4.2111.7407564 | 2021.1 211.6222.4 | |
Bumblebee \| 2021.1.1 Canary 1 | May 18, 2021 | 2021.1.1.1 AI-203.7717.56.2111.7361063 | 2020.3.3 203.7717.56 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
Arctic Fox \| 2020.3.1 Patch 4 | December 8, 2021 | 2020.3.1.26 AI-203.7717.56.2031.7935034 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Patch 3 | October 11, 2021 | 2020.3.1.25 AI-203.7717.56.2031.7784292 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Patch 2 | September 1, 2021 | 2020.3.1.24 AI-203.7717.56.2031.7678000 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Patch 1 | August 18, 2021 | 2020.3.1.23 AI-203.7717.56.2031.7621141 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 | July 28, 2021 | 2020.3.1.22 AI-203.7717.56.2031.7583922 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 RC 1 | July 20, 2021 | 2020.3.1.21 AI-203.7717.56.2031.7557721 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Beta 5 | July 2, 2021 | 2020.3.1.20 AI-203.7717.56.2031.7479365 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Beta 4 | June 16, 2021 | 2020.3.1.19 AI-203.7717.56.2031.7435690 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Beta 3 | May 27, 2021 | 2020.3.1.18 AI-203.7717.56.2031.7395685 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Beta 2 | May 20, 2021 | 2020.3.1.17 AI-203.7717.56.2031.7375522 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Beta 1 | May 18, 2021 | 2020.3.1.16 AI-203.7717.56.2031.7360992 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Canary 15 | April 29, 2021 | 2020.3.1.15 AI-203.7717.56.2031.7321754 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Canary 14 | April 7, 2021 | 2020.3.1.14 AI-203.7717.56.2031.7260174 | 2020.3.3 203.7717.56 | |
Arctic Fox \| 2020.3.1 Canary 13 | April 5, 2021 | 2020.3.1.13 AI-203.7148.57.2031.7242491 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 12 | March 25, 2021 | 2020.3.1.12 AI-203.7148.57.2031.7226969 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 11 | March 22, 2021 | 2020.3.1.11 AI-203.7148.57.2031.7209405 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 10 | March 15, 2021 | 2020.3.1.10 AI-203.7148.57.2031.7194378 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 9 | March 8, 2021 | 2020.3.1.9 AI-203.7148.57.2031.7185775 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 8 | February 24, 2021 | 2020.3.1.8 AI-203.7148.57.2031.7165533 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 7 | February 18, 2021 | 2020.3.1.7 AI-203.7148.57.2031.7136282 | 2020.3.2 203.7148.57 | |
Arctic Fox \| 2020.3.1 Canary 6 | February 10, 2021 | 2020.3.1.6 AI-203.6682.168.2031.7132434 | 2020.3.1 203.6682.168 | |
Arctic Fox \| 2020.3.1 Canary 5 | January 26, 2021 | 2020.3.1.5 AI-203.6682.168.2031.7101492 | 2020.3.1 203.6682.168 | |
2020.3.1 Canary 4 | January 6, 2021 | 2020.3.1.4 AI-202.7319.50.2031.7049475 | 2020.2.2 202.7319.50 | |
2020.3.1 Canary 3 | December 14, 2020 | 2020.3.1.3 AI-202.7319.50.2031.7019041 | 2020.2.2 202.7319.50 | |
2020.3.1 Canary 2 | December 3, 2020 | 2020.3.1.2 AI-202.7319.50.2031.7006259 | 2020.2.2 202.7319.50 | |
2020.3.1 Canary 1 | December 1, 2020 | 2020.3.1.1 AI-202.7319.50.2031.6983675 | 2020.2.2 202.7319.50 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
4.2.2 | June 30, 2021 | 4.2.2.0 AI-202.7660.26.42.7486908 | 2020.2.3 202.7660.26 | |
4.2.1 | May 13, 2021 | 4.2.1.0 AI-202.7660.26.42.7351085 | 2020.2.3 202.7660.26 | |
4.2.0 | May 4, 2021 | 4.2.0.24 AI-202.7660.26.42.7322048 | 2020.2.3 202.7660.26 | |
4.2 RC 1 | April 6, 2021 | 4.2.0.23 AI-202.7660.26.42.7231092 | 2020.2.3 202.7660.26 | |
4.2 Beta 6 | March 9, 2021 | 4.2.0.22 AI-202.7660.26.42.7188722 | 2020.2.3 202.7660.26 | |
4.2 Beta 5 | February 22, 2021 | 4.2.0.21 AI-202.7660.26.42.7141121 | 2020.2.3 202.7660.26 | |
4.2 Beta 4 | January 28, 2021 | 4.2.0.20 AI-202.7660.26.42.7094744 | 2020.2.3 202.7660.26 | |
4.2 Beta 3 | January 6, 2021 | 4.2.0.19 AI-202.7660.26.42.7033425 | 2020.2.3 202.7660.26 | |
4.2 Beta 2 | December 14, 2020 | 4.2.0.18 AI-202.7660.26.42.7008469 | 2020.2.3 202.7660.26 | |
4.2 Beta 1 | December 1, 2020 | 4.2.0.17 AI-202.7660.26.42.6987402 | 2020.2.3 202.7660.26 | |
4.2 Canary 16 | November 5, 2020 | 4.2.0.16 AI-202.7660.26.42.6939830 | 2020.2.3 202.7660.26 | |
4.2 Canary 15 | October 27, 2020 | 4.2.0.15 AI-202.7660.26.42.6922807 | 2020.2.3 202.7660.26 | |
4.2 Canary 14 | October 20, 2020 | 4.2.0.14 AI-202.7660.26.42.6907010 | 2020.2.3 202.7660.26 | |
4.2 Canary 13 | October 1, 2020 | 4.2.0.13 AI-202.7319.50.42.6863838 | 2020.2.2 202.7319.50 | |
4.2 Canary 12 | September 22, 2020 | 4.2.0.12 AI-202.6397.94.42.6847140 | 2020.2 202.6397.94 | |
4.2 Canary 11 | September 17, 2020 | 4.2.0.11 AI-202.6397.94.42.6825553 | 2020.2 202.6397.94 | |
4.2 Canary 10 | September 8, 2020 | 4.2.0.10 AI-202.6397.94.42.6811877 | 2020.2 202.6397.94 | |
4.2 Canary 9 | September 1, 2020 | 4.2.0.9 AI-202.6397.94.42.6795674 | 2020.2 202.6397.94 | |
4.2 Canary 8 | August 26, 2020 | 4.2.0.8 AI-202.6397.94.42.6787931 | 2020.2 202.6397.94 | |
4.2 Canary 7 | July 30, 2020 | 4.2.0.7 AI-201.7846.76.42.6720134 | 2020.1.2 201.7846.76 | |
4.2 Canary 6 | July 28, 2020 | 4.2.0.6 AI-201.7846.76.42.6712195 | 2020.1.2 201.7846.76 | |
4.2 Canary 5 | July 21, 2020 | 4.2.0.5 AI-201.7846.76.42.6682321 | 2020.1.2 201.7846.76 | |
4.2 Canary 4 | July 8, 2020 | 4.2.0.4 AI-201.7846.76.42.6636798 | 2020.1.2 201.7846.76 | |
4.2 Canary 3 | June 30, 2020 | 4.2.0.3 AI-201.7846.76.42.6630367 | 2020.1.2 201.7846.76 | |
4.2 Canary 2 | June 18, 2020 | 4.2.0.2 AI-201.7846.76.42.6582697 | 2020.1.2 201.7846.76 | |
4.2 Canary 1 | June 10, 2020 | 4.2.0.1 AI-201.7223.91.42.6568795 | 2020.1.1 201.7223.91 | |
4.1.3 | March 18, 2021 | 4.1.3.0 AI-201.8743.12.41.7199119 | 2020.1.4 201.8743.12 | |
4.1.2 | January 19, 2021 | 4.1.2.0 AI-201.8743.12.41.7042882 | 2020.1.4 201.8743.12 | |
4.1.1 | November 10, 2020 | 4.1.1.0 AI-201.8743.12.41.6953283 | 2020.1.4 201.8743.12 | |
4.1 | October 12, 2020 | 4.1.0.19 AI-201.8743.12.41.6858069 | 2020.1.4 201.8743.12 | |
4.1 RC 3 | September 15, 2020 | 4.1.0.18 AI-201.8743.12.41.6823847 | 2020.1.4 201.8743.12 | |
4.1 RC 2 | August 31, 2020 | 4.1.0.17 AI-201.8743.12.41.6776251 | 2020.1.4 201.8743.12 | |
4.1 RC 1 | August 10, 2020 | 4.1.0.16 AI-201.8743.12.41.6719854 | 2020.1.4 201.8743.12 | |
4.1 Beta 5 | July 23, 2020 | 4.1.0.15 AI-201.8538.31.41.6692364 | 2020.1.3 201.8538.31 | |
4.1 Beta 4 | July 16, 2020 | 4.1.0.14 AI-201.7846.76.41.6667167 | 2020.1.2 201.7846.76 | |
4.1 Beta 3 | July 7, 2020 | 4.1.0.13 AI-201.7846.76.41.6636797 | 2020.1.2 201.7846.76 | |
4.1 Beta 2 | June 25, 2020 | 4.1.0.12 AI-201.7846.76.41.6604631 | 2020.1.2 201.7846.76 | |
4.1 Beta 1 | June 10, 2020 | 4.1.0.11 AI-201.7223.91.41.6565218 | 2020.1.1 201.7223.91 | |
4.1 Canary 10 | May 26, 2020 | 4.1.0.10 AI-201.7223.91.41.6507185 | 2020.1.1 201.7223.91 | |
4.1 Canary 9 | May 7, 2020 | 4.1.0.9 AI-201.3803.71.41.6466190 | 2020.1 201.3803.71 | |
4.1 Canary 8 | April 28, 2020 | 4.1.0.8 AI-193.6911.18.41.6423924 | 2019.3.4 193.6911.18 | |
4.1 Canary 7 | April 23, 2020 | 4.1.0.7 AI-193.6911.18.41.6401718 | 2019.3.4 193.6911.18 | |
4.1 Canary 6 | April 17, 2020 | 4.1.0.6 AI-193.6911.18.41.6381907 | 2019.3.4 193.6911.18 | |
4.1 Canary 5 | April 9, 2020 | 4.1.0.5 AI-193.6911.18.41.6362631 | 2019.3.4 193.6911.18 | |
4.1 Canary 4 | March 24, 2020 | 4.1.0.4 AI-193.6494.35.41.6325121 | 2019.3.3 193.6494.35 | |
4.1 Canary 3 | March 18, 2020 | 4.1.0.3 AI-193.6494.35.41.6297379 | 2019.3.3 193.6494.35 | |
4.1 Canary 2 | March 9, 2019 | 4.1.0.2 AI-193.6494.35.41.6264773 | 2019.3.3 193.6494.35 | |
4.1 Canary 1 | February 27, 2020 | 4.1.0.1 AI-193.6494.35.41.6224510 | 2019.3.3 193.6494.35 | |
4.0.2 | October 6, 2020 | 4.0.2.0 AI-193.6911.18.40.6821437 | 2019.3.4 193.6911.18 | |
4.0.1 | July 14, 2020 | 4.0.1.0 AI-193.6911.18.40.6626763 | 2019.3.4 193.6911.18 | |
4.0 | May 28, 2020 | 4.0.0.16 AI-193.6911.18.40.6514223 | 2019.3.4 193.6911.18 | |
4.0 RC 1 | May 12, 2020 | 4.0.0.15 AI-193.6911.18.40.6453388 | 2019.3.4 193.6911.18 | |
4.0 Beta 5 | April 23, 2020 | 4.0.0.14 AI-193.6911.18.40.6401094 | 2019.3.4 193.6911.18 | |
4.0 Beta 4 | April 7, 2020 | 4.0.0.13 AI-193.6911.18.40.6348893 | 2019.3.4 193.6911.18 | |
4.0 Beta 3 | March 19, 2020 | 4.0.0.12 AI-193.6494.35.40.6296804 | 2019.3.3 193.6494.35 | |
4.0 Beta 2 | March 11, 2020 | 4.0.0.11 AI-193.6494.35.40.6254973 | 2019.3.3 193.6494.35 | |
4.0 Beta 1 | February 25, 2020 | 4.0.0.10 AI-193.6494.35.40.6220182 | 2019.3.3 193.6494.35 | |
4.0 Canary 9 | January 21, 2020 | 4.0.0.9 AI-193.5233.102.40.6137316 | 2019.3 193.5233.102 | |
4.0 Canary 8 | January 7, 2020 | 4.0.0.8 AI-193.5233.102.40.6107147 | 2019.3 193.5233.102 | |
4.0 Canary 7 | December 23, 2019 | 4.0.0.7 AI-193.5233.102.40.6085562 | 2019.3 193.5233.102 | |
4.0 Canary 6 | December 10, 2019 | 4.0.0.6 AI-193.2956.37.40.6052267 | 2019.3 193.2956.37 | |
4.0 Canary 5 | December 4, 2019 | 4.0.0.5 AI-193.2956.37.40.6039983 | 2019.3 193.2956.37 | |
4.0 Canary 4 | November 20, 2019 | 4.0.0.4 AI-192.6817.14.36.6008643 | 2019.2.3 192.6817.14 | |
4.0 Canary 3 | November 14, 2019 | 4.0.0.3 AI-192.6817.14.36.5994236 | 2019.2.3 192.6817.14 | |
4.0 Canary 2 | November 7, 2019 | 4.0.0.2 AI-192.6817.14.36.5984562 | 2019.2.3 192.6817.14 | |
4.0 Canary 1 | October 23, 2019 | 4.0.0.1 AI-192.6817.14.36.5959023 | 2019.2.3 192.6817.14 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
3.6.3 | April 17, 2020 | 3.6.3.0 AI-192.7142.36.36.6392135 | 2019.2.4 192.7142.36 | |
3.6.2 | March 31, 2020 | 3.6.2.0 AI-192.7142.36.36.6308749 | 2019.2.4 192.7142.36 | |
3.6.1 | February 28, 2020 | 3.6.1.0 AI-192.7142.36.36.6241897 | 2019.2.4 192.7142.36 | |
3.6 | February 24, 2020 | 3.6.0.21 AI-192.7142.36.36.6200805 | 2019.2.4 192.7142.36 | |
3.6 RC 3 | February 10, 2020 | 3.6.0.20 AI-192.7142.36.36.6186006 | 2019.2.4 192.7142.36 | |
3.6 RC 2 | January 29, 2020 | 3.6.0.19 AI-192.7142.36.36.6165589 | 2019.2.4 192.7142.36 | |
3.6 RC 1 | December 17, 2019 | 3.6.0.18 AI-192.7142.36.36.6071332 | 2019.2.4 192.7142.36 | |
3.6 Beta 5 | December 4, 2019 | 3.6.0.17 AI-192.6817.14.36.6018865 | 2019.2.3 192.6817.14 | |
3.6 Beta 4 | November 14, 2019 | 3.6.0.16 AI-192.6817.14.36.5994180 | 2019.2.3 192.6817.14 | |
3.6 Beta 3 | November 6, 2019 | 3.6.0.15 AI-192.6817.14.36.5982640 | 2019.2.3 192.6817.14 | |
3.6 Beta 2 | October 31, 2019 | 3.6.0.14 AI-192.6817.14.36.5947919 | 2019.2.3 192.6817.14 | |
3.6 Beta 1 | October 10, 2019 | 3.6.0.13 AI-192.6603.28.36.5916306 | 2019.2.2 192.6603.28 | |
3.6 Canary 12 | September 18, 2019 | 3.6.0.12 AI-192.6262.58.36.5871855 | 2019.2.1 192.6262.58 | |
3.6 Canary 11 | September 11, 2019 | 3.6.0.11 AI-192.6262.58.36.5863777 | 2019.2.1 192.6262.58 | |
3.6 Canary 10 | September 5, 2019 | 3.6.0.10 AI-192.5728.98.36.5842447 | 2019.2 192.5728.98 | |
3.6 Canary 9 | August 29, 2019 | 3.6.0.9 AI-192.5728.98.36.5830636 | 2019.2 192.5728.98 | |
3.6 Canary 8 | August 27, 2019 | 3.6.0.8 AI-192.5728.98.36.5825043 | 2019.2 192.5728.98 | |
3.6 Canary 7 | August 23, 2019 | 3.6.0.7 AI-192.5728.98.36.5807797 | 2019.2 192.5728.98 | |
3.6 Canary 6 | August 13, 2019 | 3.6.0.6 AI-192.5587.17.36.5787298 | 2019.2 192.5587.17 | |
3.6 Canary 5 | July 17, 2019 | 3.6.0.5 AI-191.7479.19.36.5721125 | 2019.1.3 191.7479.19 | |
3.6 Canary 4 | June 26, 2019 | 3.6.0.4 AI-191.7479.19.36.5679705 | 2019.1.3 191.7479.19 | |
3.6 Canary 3 | June 7, 2019 | 3.6.0.3 AI-191.7141.44.36.5618338 | 2019.1.2 191.7141.44 | |
3.6 Canary 2 | May 30, 2019 | 3.6.0.2 AI-191.7141.44.36.5599242 | 2019.1.2 191.7141.44 | |
3.6 Canary 1 | May 23, 2019 | 3.6.0.1 AI-191.7141.44.36.5595896 | 2019.1.2 191.7141.44 | |
3.5.3 | December 5, 2019 | 3.5.3.0 AI-191.8026.42.35.6010548 | 2019.1.4 191.8026.42 | |
3.5.2 | November 4, 2019 | 3.5.2.0 AI-191.8026.42.35.5977832 | 2019.1.4 191.8026.42 | |
3.5.1 | October 2, 2019 | 3.5.1.0 AI-191.8026.42.35.5900203 | 2019.1.4 191.8026.42 | |
3.5 | August 20, 2019 | 3.5.0.21 AI-191.8026.42.35.5791312 | 2019.1.4 191.8026.42 | |
3.5 RC 3 | August 8, 2019 | 3.5.0.20 AI-191.8026.42.35.5781497 | 2019.1.4 191.8026.42 | |
3.5 RC 2 | August 1, 2019 | 3.5.0.19 AI-191.7479.19.35.5763348 | 2019.1.3 191.7479.19 | |
3.5 RC 1 | July 17, 2019 | 3.5.0.18 AI-191.7479.19.35.5717577 | 2019.1.3 191.7479.19 | |
3.5 Beta 5 | June 26, 2019 | 3.5.0.17 AI-191.7479.19.35.5675373 | 2019.1.3 191.7479.19 | |
3.5 Beta 4 | June 7, 2019 | 3.5.0.16 AI-191.7141.44.35.5619324 | 2019.1.2 191.7141.44 | |
3.5 Beta 3 | May 28, 2019 | 3.5.0.15 AI-191.7141.44.35.5585527 | 2019.1.2 191.7141.44 | |
3.5 Beta 2 | May 20, 2019 | 3.5.0.14 AI-191.6707.61.35.5549111 | 2019.1.1 191.6707.61 | |
3.5 Beta 1 | May 7, 2019 | 3.5.0.13 AI-191.6707.61.35.5529924 | 2019.1.1 191.6707.61 | |
3.5 Canary 13 | April 24, 2019 | 3.5.0.12 AI-191.6183.87.35.5487692 | 2019.1 191.6183.87 | |
3.5 Canary 12 | April 18, 2019 | 3.5.0.11 AI-191.6183.87.35.5471097 | 2019.1 191.6183.87 | |
3.5 Canary 11 | April 15, 2019 | 3.5.0.10 AI-191.6183.87.35.5455988 | 2019.1 191.6183.87 | |
3.5 Canary 10 | April 4, 2019 | 3.5.0.9 AI-191.6183.62.35.5416148 | 2019.1 191.6183.62 | |
3.5 Canary 9 | March 28, 2019 | 3.5.0.8 AI-191.6183.20.35.5409101 | 2019.1 191.6183.20 | |
3.5 Canary 8 | March 25, 2019 | 3.5.0.7 AI-191.6014.8.35.5375575 | 2019.1 191.6014.8 | |
3.5 Canary 7 | March 11, 2019 | 3.5.0.6 AI-183.5429.30.35.5346365 | 2018.3.4 183.5429.30 | |
3.5 Canary 6 | February 26, 2019 | 3.5.0.5 AI-183.5429.30.35.5326993 | 2018.3.4 183.5429.30 | |
3.5 Canary 5 | February 21, 2019 | 3.5.0.4 AI-183.5429.30.35.5320907 | 2018.3.4 183.5429.30 | |
3.5 Canary 4 | February 14, 2019 | 3.5.0.3 AI-183.5429.30.35.5290690 | 2018.3.4 183.5429.30 | |
3.5 Canary 3 | January 31, 2019 | 3.5.0.2 AI-183.5153.38.35.5256920 | 2018.3.3 183.5153.38 | |
3.5 Canary 2 | January 24, 2019 | 3.5.0.1 AI-183.5153.38.35.5240547 | 2018.3.3 183.5153.38 | |
3.5 Canary 1 | January 15, 2019 | 3.5.0.0 AI-183.4886.37.35.5215047 | 2018.3.2 183.4886.37 | |
3.4.2 | July 9, 2019 | 3.4.2.0 AI-183.6156.11.34.5692245 | 2018.3.6 183.6156.11 | |
3.4.1 | May 15, 2019 | 3.4.1.0 AI-183.6156.11.34.5522156 | 2018.3.6 183.6156.11 | |
3.4 | April 17, 2019 | 3.4.0.18 AI-183.5429.30.34.5452501 | 2018.3.4 183.5429.30 | |
3.4 RC 3 | April 1, 2019 | 3.4.0.17 AI-183.5429.30.34.5400832 | 2018.3.4 183.5429.30 | |
3.4 RC 2 | March 14, 2019 | 3.4.0.16 AI-183.5429.30.34.5370308 | 2018.3.4 183.5429.30 | |
3.4 RC 1 | March 7, 2019 | 3.4.0.15 AI-183.5429.30.34.5341121 | 2018.3.4 183.5429.30 | |
3.4 Beta 5 | February 21, 2019 | 3.4.0.14 AI-183.5429.30.34.5310756 | 2018.3.4 183.5429.30 | |
3.4 Beta 4 | February 14, 2019 | 3.4.0.13 AI-183.5429.30.34.5304277 | 2018.3.4 183.5429.30 | |
3.4 Beta 3 | January 31, 2019 | 3.4.0.12 AI-183.5153.38.34.5256591 | 2018.3.3 183.5153.38 | |
3.4 Beta 2 | January 24, 2019 | 3.4.0.11 AI-183.5153.38.34.5240537 | 2018.3.3 183.5153.38 | |
3.4 Beta 1 | January 15, 2019 | 3.4.0.10 AI-183.4886.37.34.5217543 | 2018.3.2 183.4886.37 | |
3.4 Canary 10 | January 3, 2019 | 3.4.0.9 AI-183.4588.61.34.5202479 | 2018.3.1 183.4588.61 | |
3.4 Canary 9 | December 20, 2018 | 3.4.0.8 AI-183.4588.61.34.5186062 | 2018.3.1 183.4588.61 | |
3.4 Canary 8 | December 12, 2018 | 3.4.0.7 AI-183.4588.61.34.5173923 | 2018.3.1 183.4588.61 | |
3.4 Canary 7 | December 6, 2018 | 3.4.0.6 AI-183.4284.148.34.5159543 | 2018.3 183.4284.148 | |
3.4 Canary 6 | November 29, 2018 | 3.4.0.5 AI-183.4284.148.34.5146016 | 2018.3 183.4284.148 | |
3.4 Canary 5 | November 26, 2018 | 3.4.0.4 AI-183.4284.36.34.5141831 | 2018.3 183.4284.36 | |
3.4 Canary 4 | November 15, 2018 | 3.4.0.3 AI-183.4139.22.34.5129585 | 2018.3 183.4139.22 | |
3.4 Canary 3 | November 7, 2018 | 3.4.0.2 AI-183.3975.18.34.5112304 | 2018.3 183.3975.18 | |
3.4 Canary 2 | October 24, 2018 | 3.4.0.1 AI-183.2153.8.34.5081642 | 2018.3 183.2153.8 | |
3.4 Canary 1 | October 19, 2018 | 3.4.0.0 AI-182.4505.22.34.5070326 | 2018.2.4 182.4505.22 | |
3.3.2 | March 4, 2019 | 3.3.2.0 AI-182.5107.16.33.5314842 | 2018.2.6 182.5107.16 | |
3.3.1 | February 6, 2019 | 3.3.1.0 AI-182.5107.16.33.5264788 | 2018.2.6 182.5107.16 | |
3.3.0 | January 14, 2019 | 3.3.0.20 AI-182.5107.16.33.5199772 | 2018.2.6 182.5107.16 | |
3.3 RC 3 | December 20, 2018 | 3.3.0.19 AI-182.5107.16.33.5183351 | 2018.2.6 182.5107.16 | |
3.3 RC 2 | December 6, 2018 | 3.3.0.18 AI-182.5107.16.33.5160847 | 2018.2.6 182.5107.16 | |
3.3 RC 1 | November 26, 2018 | 3.3.0.17 AI-182.5107.16.33.5138683 | 2018.2.6 182.5107.16 | |
3.3 Beta 4 | November 12, 2018 | 3.3.0.16 AI-182.4892.20.33.5114240 | 2018.2.5 182.4892.20 | |
3.3 Beta 3 | November 7, 2018 | 3.3.0.15 AI-182.4892.20.33.5105271 | 2018.2.5 182.4892.20 | |
3.3 Beta 2 | October 30, 2018 | 3.3.0.14 AI-182.4892.20.33.5078385 | 2018.2.5 182.4892.20 | |
3.3 Beta 1 | October 19, 2018 | 3.3.0.13 AI-182.4505.22.33.5073496 | 2018.2.4 182.4505.22 | |
3.3 Canary 13 | October 2, 2018 | 3.3.0.12 AI-182.4505.22.33.5035453 | 2018.2.4 182.4505.22 | |
3.3 Canary 12 | September 26, 2018 | 3.3.0.11 AI-182.4505.22.33.5026711 | 2018.2.4 182.4505.22 | |
3.3 Canary 11 | September 19, 2018 | 3.3.0.10 AI-182.4323.46.33.5012296 | 2018.2.3 182.4323.46 | |
3.3 Canary 10 | September 12, 2018 | 3.3.0.9 AI-182.4129.33.33.4996246 | 2018.2.2 182.4129.33 | |
3.3 Canary 9 | September 11, 2018 | 3.3.0.8 AI-182.4129.33.33.4989483 | 2018.2.2 182.4129.33 | |
3.3 Canary 8 | August 29, 2018 | 3.3.0.7 AI-182.3911.36.33.4978721 | 2018.2.1 182.3911.36 | |
3.3 Canary 7 | August 23, 2018 | 3.3.0.6 AI-182.3911.36.33.4968538 | 2018.2.1 182.3911.36 | |
3.3 Canary 6 | August 16, 2018 | 3.3.0.5 AI-182.3684.101.33.4954005 | 2018.2 182.3684.101 | |
3.3 Canary 5 | August 7, 2018 | 3.3.0.4 AI-182.3684.101.33.4928781 | 2018.2 182.3684.101 | |
3.3 Canary 4 | August 2, 2018 | 3.3.0.3 AI-182.3208.16.33.4924367 | 2018.2 182.3208.16 | |
3.3 Canary 3 | July 12, 2018 | 3.3.0.2 AI-181.5281.24.33.4884283 | 2018.1.5 181.5281.24 | |
3.3 Canary 2 | July 3, 2018 | 3.3.0.1 AI-181.5281.24.33.4869471 | 2018.1.5 181.5281.24 | |
3.3 Canary 1 | June 28, 2018 | 3.3.0.0 AI-181.4892.42.33.4861037 | 2018.1.3 181.4892.42 | |
3.2.1 | October 11, 2018 | 3.2.1.0 AI-181.5540.7.32.5056338 | 2018.1.6 181.5540.7 | |
3.2.0 | September 24, 2018 | 3.2.0.26 AI-181.5540.7.32.5014246 | 2018.1.6 181.5540.7 | |
3.2 RC 3 | September 10, 2018 | 3.2.0.25 AI-181.5540.7.32.4987877 | 2018.1.6 181.5540.7 | |
3.2 RC 2 | August 24, 2018 | 3.2.0.24 AI-181.5540.7.32.4974118 | 2018.1.6 181.5540.7 | |
3.2 RC 1 | August 23, 2018 | 3.2.0.23 AI-181.5540.7.32.4963425 | 2018.1.6 181.5540.7 | |
3.2 Beta 5 | July 30, 2018 | 3.2.0.22 AI-181.5281.24.32.4913314 | 2018.1.5 181.5281.24 | |
3.2 Beta 4 | July 16, 2018 | 3.2.0.21 AI-181.5281.24.32.4886486 | 2018.1.5 181.5281.24 | |
3.2 Beta 3 | July 10, 2018 | 3.2.0.20 AI-181.5281.24.32.4868252 | 2018.1.5 181.5281.24 | |
3.2 Beta 2 | June 28, 2018 | 3.2.0.19 AI-181.5281.24.32.4860949 | 2018.1.5 181.5281.24 | |
3.2 Beta 1 | June 21, 2018 | 3.2.0.18 AI-181.4892.42.32.4847800 | 2018.1.3 181.4892.42 | |
3.2 Canary 18 | June 11, 2018 | 3.2.0.17 AI-181.4892.42.32.4830125 | 2018.1.3 181.4892.42 | |
3.2 Canary 17 | June 7, 2018 | 3.2.0.16 AI-181.4892.42.32.4823740 | 2018.1.3 181.4892.42 | |
3.2 Canary 16 | May 29, 2018 | 3.2.0.15 AI-181.4668.68.32.4802120 | 2018.1.2 181.4668.68 | |
3.2 Canary 15 | May 17, 2018 | 3.2.0.14 AI-181.4668.68.32.4773949 | 2018.1.2 181.4668.68 | |
3.2 Canary 14 | May 8, 2018 | 3.2.0.13 AI-181.4668.68.32.4763614 | 2018.1.2 181.4668.68 | |
3.2 Canary 13 | April 30, 2018 | 3.2.0.12 AI-181.4445.78.32.4749738 | 2018.1.1 181.4445.78 | |
3.2 Canary 12 | April 23, 2018 | 3.2.0.11 AI-181.4203.550.32.4729833 | 2018.1 181.4203.550 | |
3.2 Canary 11 | April 16, 2018 | 3.2.0.10 AI-181.3007.14.32.4720098 | 2018.1 181.3007.14 | |
3.2 Canary 10 | April 9, 2018 | 3.2.0.9 AI-181.2784.17.32.4705630 | 2018.1 181.2784.17 | |
3.2 Canary 9 | April 2, 2018 | 3.2.0.8 AI-173.4688006 | 2018.1 173.4688006.0 | |
3.2 Canary 8 | March 26, 2018 | 3.2.0.7 AI-173.4670218 | 2018.1 173.4670218.0 | |
3.2 Canary 7 | March 19, 2018 | 3.2.0.6 AI-173.4658582 | 2018.1 173.4658582.0 | |
3.2 Canary 6 | March 12, 2018 | 3.2.0.5 AI-173.4640885 | 2018.1 173.4640885.0 | |
3.2 Canary 5 | March 5, 2018 | 3.2.0.4 AI-173.4630681 | 2018.1 173.4630681.0 | |
3.2 Canary 4 | February 23, 2018 | 3.2.0.3 AI-173.4615518 | 2018.1 173.4615518.0 | |
3.2 Canary 3 | February 15, 2018 | 3.2.0.2 AI-173.4595177 | 2018.1 173.4595177.0 | |
3.2 Canary 2 | February 9, 2018 | 3.2.0.1 AI-173.4591728 | 2018.1 173.4591728.0 | |
3.2 Canary 1 | February 1, 2018 | 3.2.0.0 AI-173.4572754 | 2018.1 173.4572754.0 | |
3.1.4 | August 6, 2018 | 3.1.4.0 AI-173.4907809 | 2018.1 173.4907809.0 | |
3.1.3 | June 7, 2018 | 3.1.3.0 AI-173.4819257 | 2018.1 173.4819257.0 | |
3.1.2 | April 23, 2018 | 3.1.2.0 AI-173.4720617 | 2018.1 173.4720617.0 | |
3.1.1 | April 9, 2018 | 3.1.1.0 AI-173.4697961 | 2018.1 173.4697961.0 | |
3.1 | March 26, 2018 | 3.1.0.16 AI-173.4670197 | 2018.1 173.4670197.0 | |
3.1 RC 3 | March 19, 2018 | 3.1.0.15 AI-173.4658569 | 2018.1 173.4658569.0 | |
3.1 RC 2 | March 12, 2018 | 3.1.0.14 AI-173.4640767 | 2018.1 173.4640767.0 | |
3.1 RC 1 | March 5, 2018 | 3.1.0.13 AI-173.4625007 | 2018.1 173.4625007.0 | |
3.1 Beta 4 | February 23, 2018 | 3.1.0.12 AI-173.4615496 | 2018.1 173.4615496.0 | |
3.1 Beta 3 | February 15, 2018 | 3.1.0.11 AI-173.4595152 | 2018.1 173.4595152.0 | |
3.1 Beta 2 | February 9, 2018 | 3.1.0.10 AI-173.4580418 | 2018.1 173.4580418.0 | |
3.1 Beta 1 | January 30, 2018 | 3.1.0.9 AI-173.4567466 | 2018.1 173.4567466.0 | |
3.1 Canary 9 | January 24, 2018 | 3.1.0.8 AI-173.4559767 | 2018.1 173.4559767.0 | |
3.1 Canary 8 | January 12, 2018 | 3.1.0.7 AI-173.4529993 | 2018.1 173.4529993.0 | |
3.1 Canary 7 | January 5, 2018 | 3.1.0.6 AI-173.4524538 | 2018.1 173.4524538.0 | |
3.1 Canary 6 | December 20, 2017 | 3.1.0.5 AI-173.4506631 | 2018.1 173.4506631.0 | |
3.1 Canary 5 | December 5, 2017 | 3.1.0.4 AI-171.4474551 | 2017.2 171.4474551.0 | |
3.1 Canary 4 | November 17, 2017 | 3.1.0.3 AI-171.4444016 | 2017.2 171.4444016.0 | |
3.1 Canary 3 | November 10, 2017 | 3.1.0.2 AI-171.4435470 | 2017.2 171.4435470.0 | |
3.1 Canary 2 | November 3, 2017 | 3.1.0.1 AI-171.4429293 | 2017.2 171.4429293.0 | |
3.1 Canary 1 | October 25, 2017 | 3.1.0.0 AI-171.4415322 | 2017.2 171.4415322.0 | |
3.0.1 | November 20, 2017 | 3.0.1.0 AI-171.4443003 | 2017.2 171.4443003.0 | |
3.0 | October 25, 2017 | 3.0.0.18 AI-171.4408382 | 2017.2 171.4408382.0 | |
3.0 RC 2 | October 19, 2017 | 3.0.0.17 AI-171.4402976 | 2017.2 171.4402976.0 | |
3.0 RC 1 | October 13, 2017 | 3.0.0.16 AI-171.4392136 | 2017.2 171.4392136.0 | |
3.0 Beta 7 | October 3, 2017 | 3.0.0.15 AI-171.4365657 | 2017.2 171.4365657.0 | |
3.0 Beta 6 | September 15, 2017 | 3.0.0.14 AI-171.4333198 | 2017.2 171.4333198.0 | |
3.0 Beta 5 | September 8, 2017 | 3.0.0.13 AI-171.4316950 | 2017.2 171.4316950.0 | |
3.0 Beta 4 | September 1, 2017 | 3.0.0.12 AI-171.4304935 | 2017.2 171.4304935.0 | |
3.0 Beta 2 | August 11, 2017 | 3.0.0.10 AI-171.4263559 | 2017.2 171.4263559.0 | |
3.0 Beta 1 | August 9, 2017 | 3.0.0.9 AI-171.4243858 | 2017.2 171.4243858.0 | |
3.0 Canary 9 | July 31, 2017 | 3.0.0.8 AI-171.4220116 | 2017.2 171.4220116.0 | |
3.0 Canary 8 | July 24, 2017 | 3.0.0.7 AI-171.4195411 | 2017.2 171.4195411.0 | |
3.0 Canary 7 | July 17, 2017 | 3.0.0.6 AI-171.4182969 | 2017.2 171.4182969.0 | |
3.0 Canary 6 | July 10, 2017 | 3.0.0.5 AI-171.4163606 | 2017.2 171.4163606.0 | |
3.0 Canary 5 | June 30, 2017 | 3.0.0.4 AI-171.4141229 | 2017.2 171.4141229.0 | |
3.0 Canary 4 | June 15, 2017 | 3.0.0.3 AI-171.4101728 | 2017.2 171.4101728.0 | |
3.0 Canary 3 | June 2, 2017 | 3.0.0.2 AI-171.4056697 | 2017.2 171.4056697.0 | |
3.0 Canary 2 | May 26, 2017 | 3.0.0.1 AI-171.4041253 | 2017.2 171.4041253.0 | |
3.0 Canary 1 | May 17, 2017 | 3.0.0.0 AI-171.4010489 | 2017.2 171.4010489.0 |
Release Name | Channel | Release Date | Version | IntelliJ IDEA Version |
|---|---|---|---|---|
2.4 Preview 7 | April 28, 2017 | 2.4.0.6 AI-171.3934896 | 2017.2 171.3934896.0 | |
2.4 Preview 6 | April 13, 2017 | 2.4.0.5 AI-171.3909050 | 2017.2 171.3909050.0 | |
2.4 Preview 5 | April 7, 2017 | 2.4.0.4 AI-171.3883974 | 2017.2 171.3883974.0 | |
2.4 Preview 4 | April 3, 2017 | 2.4.0.3 AI-171.3870562 | 2017.2 171.3870562.0 | |
2.3.3 | June 8, 2017 | 2.3.3.0 AI-162.4069837 | 2016.3 162.4069837.0 | |
2.3.2 | May 11, 2017 | 2.3.2.0 AI-162.3934792 | 2016.3 162.3934792.0 |
IDE: Homepage (https://www.jetbrains.com/objc), Versions (https://www.jetbrains.com/objc/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/appcode)
Plugin projects targeting AppCode (https://www.jetbrains.com/objc/) can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
With the release of AppCode 2022.3, we're sunsetting the product. Please see this blog post (https://blog.jetbrains.com/appcode/2022/12/appcode-2022-3-release-and-end-of-sales-and-support/) for details.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
When targeting 2020.3, please see this migration guide (https://blog.jetbrains.com/clion/2020/12/migration-guide-for-plugins-2020-3/).
The Gradle configuration of AppCode plugin projects uses neither Product-Specific nor IntelliJ IDEA Attributes. Instead, configure AppCode plugin projects to use the intellij.localPath ("localPath" in "Gradle IntelliJ Plugin") attribute.
AppCode plugin development requires installing AppCode locally.
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute.
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.localPath ("localPath" in "Gradle IntelliJ Plugin") | Path to locally installed target version of AppCode. For example, for macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/AppCode/ch-0/193.5662.55/AppCode.app/Contents. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of AppCode. For example, for macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/AppCode/ch-0/193.5662.55/AppCode.app/Contents. |
The dependency on the AppCode APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") table, the <depends> ("depends" in "Plugin Configuration File") tags must declare com.intellij.modules.appcode module dependency, or com.intellij.appcode plugin dependency for plugins targeting only versions 2020.3+.
See AppCode Extension Point and Listener List for the complete list.
Use the Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products") process to identify the libraries in AppCode. Test your plugin with any version of AppCode you wish to support.
IDE: Homepage (https://www.jetbrains.com/clion), Versions (https://www.jetbrains.com/clion/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/clion)
CLion (https://www.jetbrains.com/clion/) is an IntelliJ Platform-based product. Plugin projects for CLion can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
When targeting 2020.3, please see this migration guide (https://blog.jetbrains.com/clion/2020/12/migration-guide-for-plugins-2020-3/).
The configuration of CLion plugin projects follows the methods described in Configuring Plugin Projects using a Product-Specific Attribute ("Configuring Plugin Projects Using a Product-Specific Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"), and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs").
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute.
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | CL for the product CLion. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the targeted CLion version, e.g. 2019.3.1. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | No specific declaration is needed. |
intellij.downloadSources ("downloadSources" in "Gradle IntelliJ Plugin") | false is required because no public source code is available. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Not needed; the Development Instance will automatically match intellij.type. |
The dependency on the CLion APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") table, the <depends> ("depends" in "Plugin Configuration File") tags must declare com.intellij.modules.clion module dependency, or com.intellij.clion plugin dependency for plugins targeting only versions 2020.3+.
See CLion Extension Point and Listener List for the complete list.
Use the Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products") process to identify the JAR files under the External Library Gradle:com.jetbrains:clion:<version>. Test your plugin with versions of CLion you intend to support.
When learning new APIs, it is helpful to have some representative projects for reference:
C/C++ Coverage (https://github.com/zero9178/C-Cpp-Coverage-for-CLion)
C/C++ Single File Execution (https://github.com/corochann/SingleFileExecutionPlugin)
IDE: Homepage (https://www.jetbrains.com/datagrip), Versions (https://www.jetbrains.com/datagrip/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/dbe)
DataGrip (https://www.jetbrains.com/datagrip/) is an IntelliJ Platform-based product. Plugin projects targeting DataGrip can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The configuration of DataGrip plugin projects follows the methods described in Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"), and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs").
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute. To see how these attributes appear in a similar Gradle build script for PhpStorm, see "Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs".
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IU for IntelliJ IDEA Ultimate. IC is incompatible with the required DatabaseTools plugin. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | 2019.3 Set to the same version as the DataGrip target version, as set by runIde.ideDir. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | DatabaseTools Dependency on the bundled DatabaseTools plugin. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of DataGrip. For example, for macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/datagrip/ch-0/193.5233.139/DataGrip.app/Contents. |
The dependency on the DataGrip APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") table, the <depends> ("depends" in "Plugin Configuration File") tags must declare com.intellij.database. Note that DataGrip plugins must also declare a dependency on com.intellij.modules.platform because com.intellij.database is not recognized as a module. Consequently, without the com.intellij.modules.platform declaration the plugin is assumed to be a legacy plugin ("Declaring Plugin Dependencies" in "Plugin Compatibility with IntelliJ Platform Products") and will not load in DataGrip.
See DataGrip Extension Point and Listener List for the complete list.
Use the Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products") process to identify the libraries in DatabaseTools. Test your plugin with any version of DataGrip you wish to support.
IDE: Homepage (https://www.jetbrains.com/go), Versions (https://www.jetbrains.com/go/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/go)
GoLand (https://www.jetbrains.com/go/) is an IntelliJ Platform-based product. Plugin projects for GoLand can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The configuration of targeting GoLand IDE follows the methods described in Configuring Plugin Projects Using a Product-Specific Attribute ("Configuring Plugin Projects Using a Product-Specific Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs").
Starting with 2020.2, it's possible to configure GO for intellij.type in the Gradle build script. If you need to use Go language APIs, specifying the GO platform type is not enough - it is required to add a dependency to the org.jetbrains.plugins.go plugin.
intellij {
version.set("2020.3")
type.set("GO")
// required if Go language API is needed:
plugins.set(listOf("org.jetbrains.plugins.go"))
}intellij {
version = '2020.3'
type = 'GO'
// required if Go language API is needed:
plugins = ['org.jetbrains.plugins.go']
}The configuration of GoLand plugin projects follows the methods described in Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"), and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs").
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute. To see how these attributes appear in a similar Gradle build script for PhpStorm, see "Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs".
The Go plugin version is explicitly declared because it isn't bundled with IntelliJ IDEA Ultimate Edition. Select a version (https://plugins.jetbrains.com/plugin/9568-go/versions) of the Go plugin compatible with the IntelliJ Idea Ultimate version.
Gradle IntelliJ Plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IU for IntelliJ IDEA Ultimate. The Go plugin isn't compatible with IntelliJ IDEA Community Edition. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the same IU BRANCH.BUILD as the GoLand target version, e.g. 193.5233.102. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | org.jetbrains.plugins.go:193.5233.102.83 for the Go plugin. See below for Go plugin version information. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of GoLand. For example, on macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/Goland/ch-0/193.5233.112/GoLand.app/Contents. |
Depending on plugin's requirements, the following <depends> ("depends" in "Plugin Configuration File") entries are needed in the plugin.xml file:
com.intellij.modules.platform - Always required. See Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs") for details.
org.jetbrains.plugins.go - Required if the Go plugin APIs are used in the plugin.
com.intellij.modules.goland (2020.2+) or com.intellij.modules.go (pre-2020.2) - Required if the plugin targets GoLand IDE only. The plugin will not be loaded in other IDEs, even if the Go plugin is present.
Depending on the com.intellij.modules.goland allows a plugin to be installed in the GoLand IDE only. However, the Go plugin can be installed in IntelliJ IDEA Ultimate (https://www.jetbrains.com/idea/) and potentially other IDEs. To make the plugin compatible with GoLand and other IDEs supporting the Go language consider depending on:
org.jetbrains.plugins.go - The plugin will be loaded only when the Go plugin is actually installed in the running IDE.
com.intellij.modules.go-capable - The plugin will be loaded in IDEs that are capable of installing the Go plugin. Note that the Go plugin doesn't have to be actually installed when this module is present.
See GoLand Extension Point and Listener List for the complete list.
Use the Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products") process to identify the library intellij-go-$version$.jar, where $version$ corresponds to the version of the Go plugin. Test your plugin with any version of GoLand you intend to support.
Please see this issue (https://github.com/JetBrains/gradle-intellij-plugin/issues/477#issuecomment-845022914) for required additional dependency setup.
When learning new APIs, it is helpful to have some representative projects for reference:
Go Method Generator (https://github.com/pkondratev/Intellij-go-method-generator)
Go Builder Generator (https://github.com/OddCN/go-builder-generator-idea-plugin)
IDE: Homepage (https://www.jetbrains.com/idea), Versions (https://www.jetbrains.com/idea/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/idea_ce)
IntelliJ IDEA (https://www.jetbrains.com/idea/) is available in two editions: IntelliJ Community Edition and IntelliJ IDEA Ultimate. See Choose your edition (https://www.jetbrains.com/idea/features/#choose-your-edition) and Feature Comparison (https://www.jetbrains.com/products/compare/?product=idea&product=idea-ce) for a detailed comparison.
The configuration of IntelliJ IDEA plugin projects follows the methods described in Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs").
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IC for IntelliJ IDEA Community Edition (default) IU for IntelliJ IDEA Ultimate |
intellij.version ("version" in "Gradle IntelliJ Plugin") | IDE version, e.g. 2022.2 |
See IntelliJ Community Plugins Extension Point and Listener List for API from bundled plugins.
IntelliJ IDEA Ultimate provides information specific to this edition.
See "Java" in "Plugin Compatibility with IntelliJ Platform Products" on how to use Java-specific functionality.
PSI Cookbook ("Java Specific" in "PSI Cookbook") lists a number of common operations for working with Java PSI.
Depending on exact functionality, a plugin can also target UAST - Unified Abstract Syntax Tree to support multiple JVM languages, including Java and Kotlin.
Relevant Extension Points:
"JavaAnalysisPlugin.xml" in "IntelliJ Community Plugins Extension Point and Listener List"
"JavaIndexingPlugin.xml" in "IntelliJ Community Plugins Extension Point and Listener List"
"JavaPlugin.xml" in "IntelliJ Community Plugins Extension Point and Listener List"
"JavaPsiPlugin.xml" in "IntelliJ Community Plugins Extension Point and Listener List"
IDE: Homepage (https://www.jetbrains.com/idea), Versions (https://www.jetbrains.com/idea/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/idea)
IntelliJ IDEA Ultimate comes with a number of additional features and bundled plugins. See Choose your edition (https://www.jetbrains.com/idea/features/#choose-your-edition) and Feature Comparison (https://www.jetbrains.com/products/compare/?product=idea&product=idea-ce) for a detailed comparison.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
See Open Source Plugins Extension Point and Listener List on how to extend bundled plugins.
The following pages describe targeting APIs which are available only in IntelliJ IDEA Ultimate:
Tomcat Integration (Tomcat Integration)
Spring API (Spring API)
The source code of the Tomcat plugin included in IntelliJ IDEA Ultimate is available as a sample for implementing application server integration plugins. You can find the code under lib/src/src_tomcat.zip in the main IntelliJ IDEA Ultimate distribution.
Spring API allows 3rd party plugins to re-use, integrate with, or extend existing Spring Framework support in IntelliJ IDEA Ultimate.
Please see Spring Framework Support (https://www.jetbrains.com/lp/intellij-frameworks/) for general feature overview.
A popular plugin using Spring API is hybris integration (https://plugins.jetbrains.com/plugin/7525-hybris-integration).
To develop plugins, you will need to use IntelliJ IDEA Ultimate Edition version 13.1 or higher.
Setup Gradle build script ("IntelliJ Platform Configuration" in "Configuring Gradle IntelliJ Plugin") to target IntelliJ IDEA Ultimate, then add dependency (Plugin Dependencies) to bundled Spring plugin with ID com.intellij.spring.
Please use only Spring-related functionality exposed in spring-api.jar (sources are provided in $IDEA_HOME$/lib/src/src_spring-boot-openapi.zip) in your plugin. Using any other "internal" (implementation) classes from Spring plugin itself (spring.jar) is not supported.
Add <depends>com.intellij.spring</depends> to your plugin.xml (Plugin Configuration File) to require "Spring Support" plugin to be activated. All available extension points are provided under com.intellij.spring prefix. Note that the "Spring Support" plugin itself has dependencies on a few other plugins which need to be enabled in your sandbox (see notifications on startup).
A Spring facet can be attached to a Module. Nearly all Spring functionality requires an existing and correctly setup Spring facet.
Spring facets usually contain one more user-configured or automatically provided filesets, which group a set of Spring related configuration files (XML, Code, .properties, or other configuration files).
A fileset usually corresponds to an actual application context configuration at runtime. Hierarchies can be modeled by depending on another fileset (possibly from another module).
As an API-user, you will usually prefer working with SpringModel, which is built on top of fileset(s).
2017.3: LocalXmlModel#setActiveProfiles & LocalAnnotationModel#setActiveProfiles have been deprecated and will be removed in 2018.1.
Starting with 2016.2, the internal representation of bean type has been changed from PsiClass to PsiType, please note deprecations.
Some core classes have been changed in 14(.1); please see " Version 14(.1) " notes for info on how to replace existing API-calls.
See Spring API Extension Point and Listener List for the complete list.
To check availability of Spring/Spring Facet etc. see com.intellij.spring.model.utils.SpringCommonUtils.
2016.2 See com.intellij.spring.SpringLibraryUtil to obtain information about the exact version of Spring in use.
See SpringManager#getSpringModel(s)... and com.intellij.spring.model.utils.SpringModelUtils.
See com.intellij.spring.SpringModelProvider to provide implicit filesets (e.g. provided by another framework in a specific configuration file).
Version 15 See com.intellij.spring.facet.SpringAutodetectedFileSet for a convenient base class. Please note that autodetected filesets cannot be edited/modified by users in Spring facet.
2017.1 See com.intellij.spring.facet.SpringFileSetEditorCustomization to customize presentation and/or add extra settings/actions for specific autodetected filesets.
See com.intellij.spring.model.jam.CustomComponentsDiscoverer or com.intellij.spring.model.SpringImplicitBeansProviderBase to provide implicit (framework-specific) beans (e.g. "servletContext" by Spring MVC).
Version 15 CustomComponentsDiscoverer has been split into com.intellij.spring.model.custom.CustomLocalComponentsDiscoverer and com.intellij.spring.model.custom.CustomModuleComponentsDiscoverer respectively.
Version 14 See com.intellij.spring.model.scope.SpringCustomBeanScope to provide custom (e.g. framework specific) bean scopes.
Version 14.1 com.intellij.spring.profiles.SpringProfilesFactory
com.intellij.spring.CommonSpringModel#findBeanByName
Version 14: com.intellij.spring.model.utils.SpringModelSearchers#findBean
Choose one of com.intellij.spring.CommonSpringModel#findBeansByPsiClassXXX variants (please note deprecated methods).
Version 14: com.intellij.spring.model.utils.SpringModelSearchers#findBeans
Version 16: note deprecation of SpringModelSearchParameters.BeanClass#withInheritors(GlobalSearchScope)
Version 14: com.intellij.spring.model.utils.SpringModelSearchers#doesBeanExist (please note deprecated methods)
Version 14: implement SpringInfrastructureBean, such beans obtain a special icon and can be filtered in various places in UI.
All support for XML-based Spring configuration files is provided via DOM-API (XML DOM API).
See com.intellij.spring.customNamespaces EP, registered namespace-key must match the one registered with your DOM elements via @Namespace. Register available elements via standard DomExtender<Beans> EP or com.intellij.spring.dom.SpringCustomNamespaces#registerExtensions (Version 14).
Please pay attention to getModelVersion and getStubVersion (see Javadoc).
Use the following template:
@Convert(SpringBeanResolveConverter.class)
@RequiredBeanType("fqn.to.required.class") // optional
GenericAttributeValue<SpringBeanPointer> getMyAttributeName();Version 14
JamStringAttributeMeta.Single<SpringBeanPointer> ATTRIBUTE_META =
JamAttributeMeta.singleString("attributeName",
new SpringBeanReferenceJamConverter("fqn.to.required.class"));Version 16 See com.intellij.spring.model.aliasFor.SpringAliasForUtils to obtain corresponding @AliasFor JAM.
Version 15 See com.intellij.spring.spi.SpringSpiManager.
Add additional inspections (e.g. for custom namespace) to Spring Validator (Settings|Compiler|Validation) via com.intellij.spring.SpringInspectionsRegistry$Contributor in com.intellij.spring.inspectionsRegistryContributor extension point.
Version 14.1 Additional files to be processed by inspections registered with Spring Validator (e.g. specific .properties configuration files) can be registered via com.intellij.spring.SpringInspectionsRegistry$AdditionalFilesContributor
Use com.intellij.spring.facet.SpringConfigurator to provide "automatic" configuration when Spring facet is added via framework wizard.
Please do not reference bean icons from SpringApiIcons directly, but use SpringPresentationProvider to re-use unified icon/bean name. See SpringBeansPsiElementCellRenderer for popup/list renderer.
2018.1
Spring Boot API allows extending/accessing Spring Boot specific support in the IDE.
While we try to maintain compatibility, please be prepared for a less strict policy.
Add dependency (Plugin Dependencies) to bundled Spring Boot plugin with ID com.intellij.spring.boot. Sources for Spring Boot API are available in $IDEA_HOME$/lib/src/src_spring-boot-openapi.zip.
Add <depends>com.intellij.spring.boot</depends> to your plugin.xml (Plugin Configuration File) to require "Spring Boot" plugin to be activated. All available extension points are provided under com.intellij.spring.boot prefix.
Use com.intellij.spring.boot.library.SpringBootLibraryUtil to query version and availability of common additional libraries.
com.intellij.spring.boot.model.SpringBootModelConfigFileContributor allows adding support for custom config file formats.
Existing Condition implementations can be simulated at design time in IDE via com.intellij.spring.boot.model.autoconfigure.conditions.ConditionalContributor.
Custom @ConditionalOn... annotations implementing com.intellij.spring.boot.model.autoconfigure.conditions.jam.ConditionalOnJamElement will be added into evaluation automatically.
com.intellij.spring.boot.initializr.SpringInitializrModuleBuilderPostTask allows performing custom setup steps after creation of module (e.g. setup integration with build system).
Use com.intellij.spring.boot.run.endpoint extension point to add custom actuator endpoint tabs. Any settings should be exposed in "Spring Boot" settings tab via com.intellij.spring.boot.run.endpointTabConfigurable EP.
IDE: Homepage (https://www.jetbrains.com/phpstorm), Versions (https://www.jetbrains.com/phpstorm/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/phpstorm)
PhpStorm (https://www.jetbrains.com/phpstorm/) is an IntelliJ Platform-based product. This page describes configuring plugin projects targeting PhpStorm.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
See also:
Please join the dedicated intellij-php (https://jetbrains-platform.slack.com/archives/C5P9YB0LT/p1653913208725609) Slack channel to discuss PHP related plugin development.
The configuration of targeting PhpStorm IDE follows the methods described in Configuring Plugin Projects Using a Product-Specific Attribute ("Configuring Plugin Projects Using a Product-Specific Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs").
The possibility of configuring the PS IntelliJ Platform type was introduced in Gradle IntelliJ Plugin 1.8.0. Only the versions of PhpStorm 2022.2 and newer are supported.
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute.
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | PS for PhpStorm. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the targeted PS version (only versions 2022.2+ are supported). |
PhpStorm plugins targeting versions older than 2022.2 are developed using the Ultimate Edition of IntelliJ IDEA. The IntelliJ IDEA Ultimate Edition (with the PHP plugin) must be used for developing PhpStorm plugins because the PHP plugin is incompatible with IntelliJ IDEA Community Edition. However, this IntelliJ IDEA Ultimate configuration runs the risk of accidentally using some APIs that are not available in PhpStorm. The recommended best practice is to use PhpStorm for testing.
Configuration of a Gradle-based PhpStorm plugin project is used as a tutorial in the section Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"). Many techniques are discussed, such as choosing a version of IntelliJ IDEA Ultimate given a targeted version of PhpStorm.
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute. To see how these attributes appear in the Gradle build script for PhpStorm, see "Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs".
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IU for IntelliJ IDEA Ultimate. The required PHP plugin isn't compatible with IntelliJ IDEA Community Edition. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the same IU BRANCH.BUILD as the PhpStorm target version, e.g. 193.5233.102. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | com.jetbrains.php:193.5233.102 for the PHP plugin. See below for PHP plugin version information. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of PhpStorm. For example, on macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/PhpStorm/ch-0/193.5233.101/PhpStorm.app/Contents. |
The PHP plugin version is explicitly declared because it isn't bundled with IntelliJ IDEA Ultimate Edition. Select a version (https://plugins.jetbrains.com/plugin/6610-php/versions) of the PHP plugin compatible with the intellij.version ("version" in "Gradle IntelliJ Plugin").
The dependency on the PHP plugin APIs (com.jetbrains.php) must be declared in the plugin.xml (Plugin Configuration File) file, as shown in the tutorial Configuring plugin.xml ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs") section.
Please join the dedicated intellij-php (https://jetbrains-platform.slack.com/archives/C5P9YB0LT/p1653913208725609) Slack channel to discuss PHP related plugin development.
<depends>com.jetbrains.php</depends>com.jetbrains.php.lang.psi.elements.*;
com.jetbrains.php.lang.psi.elements.impl.PhpFilePathUtils contains helper methods for working with paths from PSI elements.
getFileName() returns a constant string representation of the path from the PSI element.
For example, for the expression:
// in file: /bin/folder/file.php
__DIR__ . "/file2.php";it returns "/bin/folder/file2.php".
Note that the element passed as an argument can contain any expression that can be statically evaluated to a constant.
getReferences() returns all path references from the PSI element.
Using this method in conjunction with PsiReferenceContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/psi/PsiReferenceContributor.java) you can add autocompletion for string literals in the specific contexts. For example, if certain PHP functions in your code accept paths, you can autocomplete them when writing arguments.
Note that PhpStorm automatically adds references for concatenation expressions with __DIR__ or dirname(__FILE__) function call.
See PHP Extension Point and Listener List for the complete list.
Type inference in PhpStorm is built on top of type providers, each of which is responsible for inferring the types of specific PSI elements. For example, com.jetbrains.php.lang.psi.resolve.types.PhpArrayAccessTP is responsible for inferring the types of expressions like $arr[10]. There are dozens of such providers, and they all work one after another to provide type information when needed.
All providers inherit from com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider4, which is registered in the com.jetbrains.php.typeProvider4 extension point.
The first phase of type inference takes place at the indexing stage. At this phase, PhpStorm calls PhpTypeProvider4.getType() on each type provider. PhpStorm only has access to local information from the current file and can't use information from other files as well as indexes because it doesn't yet build them. Sometimes, it can deduce the exact type from this information, but in other cases this is impossible because PhpStorm requires information from other files.
Because of this, there are two kinds of types in PhpStorm:
Complete types
Incomplete types
Complete types are types that are known exactly based on only the local information of the current file.
<?php
$a = 100;The type of the expression that's assigned to the $a variable is Complete type int, since PhpStorm can definitely infer that a numeric literal is of type int.
<?php
function foo(string $a): string {
echo $a;
}Here, since the $a parameter has a string type hint, PhpStorm can infer the Complete type string.
Incomplete types are types that need additional information from other project files besides the containing file.
Suppose we have two files:
foo.php:
<?php
function foo(): string {
return "Hello World!";
}main.php:
<?php
$a = foo();
echo $a;In the main.php file, we call the foo(), which is defined in another foo.php file. Because of this, PhpStorm won't be able to infer the type of the $a variable during the indexing stage, since it depends on the definition of the foo() from another file.
For such cases, PhpStorm will create an Incomplete type in which writes all the necessary information to resolve the type when it finishes indexing. In this case, it's the name of the function being called, so PhpStorm will create an Incomplete type #F\foo.
At the beginning there is a # character, which is a marker that the type is Incomplete.
It's followed by the type provider's unique key. PhpStorm uses this key to decide which type provider to pass the Incomplete type to resolve.
The rest of the line is the encoded information. In the example above, this is the fully qualified name of the function.
PhpStorm will further pass the created Incomplete type after it finishes the indexing to resolve it into a Complete type. It will choose instance of PhpTypeProvider4 that have PhpTypeProvider4.getKey() equal to the character after #. In other words, same provider is supposed to provide incomplete type starting with # + result of call PhpTypeProvider4.getKey(), this will result in that this type will be passes to the same PhpTypeProvider4.complete() instance during resolve.
During indexing, PhpStorm collects information about all types of elements in this way and stores them in the index. When there is a need for the type of some expression, PhpStorm passes the Incomplete type obtained at the indexing stage for resolving.
The second phase of type inference is the global Incomplete type resolution. At this phase, PhpStorm calls PhpTypeProvider4.complete() of each type provider. All Incomplete types are passed to the providers that created them. At this point, PhpStorm can access any information from other files to resolve the Incomplete type.
Since PHP is a dynamically typed language, at the type inference stage PhpStorm can get a situation where the type can be either one or the other.
For example:
<?php
$a = 100;
if (rand(0, 10) > 5) {
$a = "Hello World!";
}
$a; // (1)In (1), the variable $a will be of type int|string because PhpStorm can't deduce exactly which branch the execution will take.
PhpStorm stores this types as separate strings inside the PhpType class. Each of the types can be either Complete or Incomplete. In the Incomplete type resolving process, PhpStorm will resolve each union type individually.
Since some providers may return types for the same PSI element, union types may appear for some elements.
PhpStorm uses the com.jetbrains.php.lang.psi.resolve.types.PhpType class to work with types.
To add types to it, use add(), which can take either another PhpType or a string.
To check that a type is Complete, use isComplete().
To resolve the Incomplete type, use global(). This method shouldn't be used during indexing, namely inside PhpTypeProvider4.getType().
In PhpStorm, PSI elements with types implement the com.jetbrains.php.lang.psi.elements.PhpTypedElement interface. To get the type of element, use the getType().
PhpTypeProvider4 interface:
/**
* Extension point to implement to provide Type information on various PhpPsiElements.
*/
public interface PhpTypeProvider4 {
ExtensionPointName<PhpTypeProvider4> EP_NAME = ExtensionPointName.create("com.jetbrains.php.typeProvider4");
/**
* @return Your custom signature key, i.e. "Я". Do not use any of PhpTypeSignatureKey.XXX constants though!
*/
char getKey();
/**
* @param element to deduce type for - using only LOCAL info. <b>THIS IS MOST CRUCIAL ASPECT TO FOLLOW</b>
* @return type for element, null if no insight. You can return a custom signature here to be later decoded by getBySignature.
*/
@Nullable
PhpType getType(PsiElement element);
/**
* @param expression to complete - Here you can use index lookups
* @param project well so you can reach the PhpIndex based stuff
* @return type for element, null if no insight. You can return a custom signature here to be later decoded by getBySignature.
*/
@Nullable
PhpType complete(String expression, Project project);
/**
* Here you can extend the signature lookups
* @param expression Signature expression to decode. You can use PhpIndex.getBySignature() to look up expression internals.
* @param visited Recursion guard: please pass this on into any phpIndex calls having same parameter
* @param depth Recursion guard: please pass this on into any phpIndex calls having same parameter
* @param project well so you can reach the PhpIndex
* @return null if no match
*/
Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project);
default boolean emptyResultIsComplete() {
return false;
}
@Internal
default boolean interceptsNativeSignature() {
return false;
}
}To implement PhpTypeProvider4, you need to override 4 methods:
getKey() returns a character that will be unique for this type provider. This can be any character, as long as it's unique, for example, PhpStorm uses hieroglyphs. See also com.jetbrains.php.lang.psi.resolve.types.PhpCharBasedTypeKey.
When choosing a provider key, keep in mind that other plugins may already be using it. In this case, there will be an error stating that the key is duplicated, which will lead to incorrect work of both plugins.
getType() returns the type of the expression for the given element. It's called at the indexing stage, and therefore its implementation can't access any information from the index and must rely only on local information. If you need some information, then pack the required data into a string and return an Incomplete type based on this string.
complete() resolves an Incomplete type into a Complete type. All strings of Incomplete types are sequentially passed to this method, it should return a Complete type for them.
getBySignature() provides additional elements or references.
You can also override the emptyResultIsComplete(), which indicates whether the null returned from the complete() is a valid result, which means that PhpStorm won't add the mixed to the resulting type.
The goal of this example is to provide types for field references assigned in setUp method if containing class is PHPUnit one.
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.MultiMap;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.lang.psi.elements.impl.PhpClassImpl;
import com.jetbrains.php.phpunit.PhpUnitUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Set;
public class PhpUnitFiledInitializedInSetUpMethodsTP implements PhpTypeProvider4 {
@Override
public char getKey() {
return 'Ю';
}
@Nullable
@Override
public PhpType getType(PsiElement element) {
if (element instanceof Field) {
PhpClass phpClass = ((Field) element).getContainingClass();
if (phpClass != null) {
MultiMap<String, AssignmentExpression> accessMap = PhpClassImpl.getPhpUnitSetUpAssignmentsPerField(phpClass);
if (accessMap.containsKey(((Field) element).getName())) {
Collection<AssignmentExpression> expressions = accessMap.get(((Field) element).getName());
for (AssignmentExpression expression : expressions) {
PhpType type = expression.getType();
if (!type.isEmpty()) {
PhpType phpType = new PhpType();
String classFQN = phpClass.getFQN();
for (String t : type.getTypes()) {
phpType.add("#" + getKey() + classFQN + getKey() + t);
}
return phpType;
}
}
}
}
}
return null;
}
@Nullable
@Override
public PhpType complete(String expression, Project project) {
int indexOfSign = expression.indexOf(getKey());
int indexOfDelimiter = expression.indexOf(getKey(), indexOfSign + 1);
String classFqn = expression.substring(indexOfSign + 1, indexOfDelimiter);
String type = expression.substring(indexOfDelimiter + 1);
if (isPhpUnitClass(project, classFqn)) {
return new PhpType().add(type);
}
return null;
}
static boolean isPhpUnitClass(Project project, @NotNull String classFqn) {
return PhpIndex.getInstance(project).getClassesByFQN(classFqn).stream().anyMatch(PhpUnitUtil::extendsRootTestClass);
}
@Override
public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) {
return null;
}
}The PhpUnitFiledInitializedInSetUpMethodsTP implementation is registered with the IntelliJ Platform in the plugin configuration file using the com.jetbrains.php.typeProvider4 extension point.
<extensions defaultExtensionNs="com.jetbrains">
<php.typeProvider4
implementation="com.jetbrains.php.lang.psi.resolve.types.PhpUnitFiledInitializedInSetUpMethodsTP"/>
</extensions>The following pages list the breaking changes in IDE releases with required/recommended steps to take by plugin authors.
Changes in 2020.3 (Incompatible PHP OpenAPI changes in PhpStorm 2020.3)
Changes in 2020.2 (Incompatible PHP OpenAPI changes in PhpStorm 2020.2)
PhpStorm 2020.3 introduces support for the upcoming PHP 8, which results in several noticeable changes in the PhpStorm internals.
In PHP 8, the throw expression has been converted to a statement (https://wiki.php.net/rfc/throw_expression), so that it can now be used in arrow functions and coalescing and ternary expressions.
The following changes were introduced:
The PSI element com.jetbrains.php.lang.psi.elements.PhpThrow is now deprecated, and the new PSI element com.jetbrains.php.lang.psi.elements.PhpThrowExpression is introduced instead.
We encourage using PhpThrowExpression, since such expressions can appear in more places.
PhpThrow PSI layout has changed: it is now a Statement with a single child PhpThrowExpression. The PhpThrow#getArgument delegates to PhpThrowExpression#getArgument for now.
In PhpStorm 2020.2, the PhpThrow class will be removed and replaced with StatementImpl.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54357) for details.
Previously supported only in argument lists, trailing commas can now be used in parameter lists (https://wiki.php.net/rfc/trailing_comma_in_parameter_list), as well.
The following changes were introduced:
The last psi-element of parameter lists can now be PhpTokenTypes.opCOMMA. This might affect getting the last parameter: earlier the last psi-element of ParameterList was always a parameter but now it can also be a comma.
If you are using PsiElement.getLastChild() to get the last parameter, it is now recommended replacing it with getting a parameter by index via ParameterList.getParameter(int).
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54481) for details.
With this PHP 8 change, it is now possible to catch exceptions without capturing them to variables (https://wiki.php.net/rfc/non-capturing_catches).
The following changes were introduced:
Catch now might not have a variable inside. The @Nullable annotation for the method Catch.getException() that returns a variable was already available. However, if you are getting a variable from Catch differently, or ignoring the @Nullable annotation, you need to be aware of this case effective since PHP 8.0.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54484) for details.
The new shorthand syntax allows defining class properties by "promoting" the constructor parameters (https://wiki.php.net/rfc/constructor_promotion) with a visibility keyword (public, protected, or private).
The following changes were introduced:
com.jetbrains.php.lang.psi.elements.Parameter now can contain one of com.jetbrains.php.lang.lexer.PhpTokenTypes#tsVARIABLE_MODIFIERS as a child element.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54485) for details.
The new nullsafe operator (https://wiki.php.net/rfc/nullsafe_operator) ?-> with full short-circuiting allows applying the null-coalescing behavior to method calls, thus eliminating the need for additional null checks.
The following changes were introduced:
com.jetbrains.php.lang.psi.elements.MemberReference now has an optional child com.jetbrains.php.lang.lexer.PhpTokenTypes#opQUEST between the class reference and com.jetbrains.php.lang.lexer.PhpTokenTypes#ARROW.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54639) for details.
With named parameters (https://wiki.php.net/rfc/named_params), you can pass arguments to a function based on the parameter name, rather than its position.
The following changes were introduced:
The layout of com.jetbrains.php.lang.psi.elements.ParameterList for call sites has changed. Earlier, it was a comma-separated list of arguments; now, each argument can have optional previous siblings: com.jetbrains.php.lang.lexer.PhpTokenTypes#IDENTIFIER and com.jetbrains.php.lang.lexer.PhpTokenTypesopCOLON.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54640) for details.
With attributes (https://wiki.php.net/rfc/attributes_v2), you can provide structured, syntactic metadata to declarations of classes, properties, functions, methods, parameters, and constants.
The following changes were introduced:
New PSI elements PhpAttribute and PhpAttributesList were added.
Possible attributes' owners (com.jetbrains.php.lang.psi.elements.Function, com.jetbrains.php.lang.psi.elements.Parameter, and so on) can now have zero or more instances of PhpAttributesList as first children. This means that now it is not safe to assume that PhpPsiElement#getFirstChild will return some token. We encourage finding the needed tokens by IElementType manually.
If a PHPDoc comment of a named element is preceded by PhpAttribute, it will not be a sibling of a named element, but rather a child. We encourage using PhpNamedElement#getDocComment (which is already updated to reflect the changes) instead of finding the PHPDoc comment manually.
The stub tree structure has changed for some elements. If this causes problems in plugins, consider using higher-level facilities or contact support (https://www.jetbrains.com/help/phpstorm/getting-started.html#contact-support) for additional help.
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-53163) for details.
New match expressions (https://wiki.php.net/rfc/match_expression_v2) provide functionality similar to switch, but with safer semantics and the ability to return values.
The following changes were introduced:
New PSI elements: PhpMatchExpression and PhpMatchArm.
A list of match arms is obtained via PhpMatchExpression#getMatchArms(). The default match arm is not included in this list and is obtained by calling PhpMatchExpression#getDefaultMatchArm().
Each PhpMatchArm provides a list of conditions via PhpMatchArm#getConditions(). The method returns an empty list for the default match arm.
Match arm body expression is obtained by invoking PhpMatchArm#getBodyExpression().
See the YouTrack issue (https://youtrack.jetbrains.com/issue/WI-54356) for details.
The following changes were introduced:
PSI layout of Twig variables and fields has been changed. Earlier, Twig variables and fields used to be parsed as PsiIdentifier; now they are wrapped into new PSI elements: TwigVariable and TwigField.
TwigField may be nested and contain a variable or other fields inside.
PhpStorm 2020.2 introduced support for PHP union types (https://wiki.php.net/rfc/union_types_v2), which resulted in some PSI-breaking changes.
In earlier versions, type hints in parameters, properties, and return types were parsed inconsistently:
Return types used a separate PhpReturnType wrapper element, which contained the nullability question mark and the actual type's class reference.
Parameters and properties used no wrapper element: class references and nullability question mark were plain children of Parameter or CLASS_FIELDS.
As of PhpStorm 2020.2, class references with the question mark are uniformly wrapped into the PhpTypeDeclaration element, which is the parent for PhpReturnType, PhpFieldType, and PhpParameterType.
If your existing code fetches a class reference directly from the parent element, it is now required to get PhpTypeDeclaration first, and then call PhpTypeDeclaration#getClassReferences().
Before 2020.2:
private void handleParameterBefore(Parameter parameter) {
ClassReference classReference = PsiTreeUtil.getChildOfType(parameter, ClassReference.class);
handleReference(classReference);
}After 2020.2:
private void handleParameterAfter(Parameter parameter) {
PhpTypeDeclaration typeDeclaration = PsiTreeUtil.getChildOfType(parameter, PhpTypeDeclaration.class);
for (ClassReference classReference : typeDeclaration.getClassReferences()) {
handleReference(classReference);
}
}As of PhpStorm 2020.2, PhpReturnType.getClassReference() is deprecated, since there can be multiple class references. This method also became nullable, since in earlier versions an incomplete ? type was parsed just as a question mark, but now it is parsed as PhpTypeDeclaration with empty getClassReferences().
Before 2020.2:
private void handleReturnTypeBefore(PhpReturnType returnType) {
ClassReference classReference = returnType.getClassReference();
handleReference(classReference);
}After 2020.2:
private void handleReturnTypeAfter(PhpReturnType returnType) {
for (ClassReference classReference : returnType.getClassReferences()) {
handleReference(classReference);
}
}This page lists some example PhpStorm plugins created by third-party developers.
The Symfony plugin is a free plugin for Symfony framework developers.
There are lots of features included into this plugin, such as Symfony-specific completion and navigation for symbols, templates, service container, Doctrine, translations, routes, forms, events, and more.
Official Symfony plugin website (https://symfony2-plugin.espend.de/)
GitHub (https://github.com/Haehnchen/idea-php-symfony2-plugin)
Symfony Development in PhpStorm (https://www.jetbrains.com/help/phpstorm/symfony-support.html)
Symfony plugin on JetBrains Marketplace (https://plugins.jetbrains.com/plugin/7219-symfony-support)
The Laravel plugin is a free plugin for Laravel developers.
It provides code completion and navigation for various Laravel components: controllers, routes, views, configuration, services, and translations. You can also use Laravel-specific live templates for generating various Laravel entities.
GitHub (https://github.com/Haehnchen/idea-php-laravel-plugin)
Laravel Development in PhpStorm (https://www.jetbrains.com/help/phpstorm/laravel.html)
The Laravel Idea plugin is a paid plugin for Laravel developers.
It provides a wide array of code generation and code completion features and includes hundreds of useful helpers, including configuration and translation keys, gates names completion, blade support, etc.
Documentation (https://laravel-idea.com/docs/3.x/overview)
Issue Tracker (https://github.com/laravel-idea/plugin/issues)
Laravel Idea plugin on JetBrains Marketplace (https://plugins.jetbrains.com/plugin/13441-laravel-idea)
Laravel Development in PhpStorm (https://www.jetbrains.com/help/phpstorm/laravel.html)
Magicento is a paid PhpStorm plugin for Magento developers. A free limited version is available. Features include: Goto for factories and template paths, autocomplete for factories, XML files and class names, documentation for XML nodes, evaluation of PHP code inside the Magento environment, and much more to come!
Official Magicento website (https://magicento.com/)
Magicento on JetBrains Marketplace (https://plugins.jetbrains.com/plugin/7089-magicento)
YiiStorm is a plugin for PhpStorm IDE that is adding code navigation enhancements for Yii framework based projects. Features are: Going from render and renderPartial to the view file. Includes controllers, partials, and widgets. Supports all ways of specifying a view: themes, smarty .tpl views and external actions; Going from model name in relations() to the model class; Going from the $this->widget('path.to.widget.Class') call to the widget class; Going from controller actions() to action class.
GitHub (https://github.com/cmazx/yiistorm)
YiiStorm on JetBrains Marketplace (https://plugins.jetbrains.com/plugin/7182-yiistorm)
Nette is a family of mature and stand-alone components for PHP that create a framework.
Official Nette website (https://nette.org/en/)
Name | Description | Website | GitHub | Plugin |
|---|---|---|---|---|
Nette framework helpers | Nette framework helpers make application development in Nette easier. The plugin provides code completion and navigation for Nette components, annotations, and DI service methods in PHP code. It also implements PHP type by PhpTypeProvider. | Nette web (https://nette.org/en) | intellij-nette (https://github.com/nette-intellij/intellij-nette) | Nette plugin (https://plugins.jetbrains.com/plugin/7231-nette-framework-helpers) |
Latte | Nette Latte plugin supports Latte language – the safest & truly intuitive templates for PHP. The plugin implements custom language. Partially support PHP like in .php files (code completion, inspections, references to classes, methods, properties, etc.). Supports load config from XML files located in the project. It implements Stub indexes and more around custom language. | Latte web (https://latte.nette.org/en) | intellij-latte (https://github.com/nette-intellij/intellij-latte) | Latte plugin (https://plugins.jetbrains.com/plugin/7457-latte) |
Neon | Nette Neon is commonly used for any structured data, such as settings, language translations, etc. It is very similar to YAML. The plugin implements custom language. Easy implementation for Go to PHP classes and completion for classes. | Neon web (https://ne-on.org) | intellij-neon (https://github.com/nette-intellij/intellij-neon) | Neon plugin (https://plugins.jetbrains.com/plugin/7060-neon-support) |
Nette Tester | Nette Tester is a simple and yet convenient PHP code testing tool. The plugin implements "run configurations" and "run line markers". Supports run configurations in a remote interpreter (eg: in Docker or docker-compose). | Tester web (https://tester.nette.org/en) | intellij-nette-tester (https://github.com/nette-intellij/intellij-nette-tester) | Tester plugin (https://plugins.jetbrains.com/plugin/8226-nette-tester) |
The PHP Annotations plugin extends PhpStorm to support annotations in PHPDoc blocks.
PHP Annotations on JetBrains Marketplace (https://plugins.jetbrains.com/plugin/7320-php-annotations)
GitHub (https://github.com/Haehnchen/idea-php-annotation-plugin)
IDE: Homepage (https://www.jetbrains.com/pycharm), Versions (https://www.jetbrains.com/pycharm/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/pycharm_ce)
PyCharm (https://www.jetbrains.com/pycharm/) is an IntelliJ Platform-based product. Plugin projects for PyCharm can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The configuration of PyCharm plugin projects follows the methods described in Configuring Plugin Projects using a Product-Specific Attribute ("Configuring Plugin Projects Using a Product-Specific Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"), and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs"). The table below summarizes the Gradle IntelliJ Plugin attributes to set in the Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute.
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | PY for PyCharm Professional Edition, or PC for PyCharm Community Edition. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the targeted PY or PC version. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | Pythonid for PY/PythonCore for PC. |
intellij.downloadSources ("downloadSources" in "Gradle IntelliJ Plugin") | false is required because no public source code is available. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Not needed; the Development Instance will automatically match intellij.type. |
The dependency on the PyCharm APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs"), the <depends> ("depends" in "Plugin Configuration File") tags must declare com.intellij.modules.python.
See the SDK code sample pycharm_basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/product_specific/pycharm_basics/) for an example configuration. Please note that this code sample must be imported into Gradle explicitly, as it is not included in the _gradleCompositeBuild.
See IntelliJ Community Plugins Extension Point and Listener List for PyCharm Community.
The plugin Python (https://plugins.jetbrains.com/plugin/631-python) (Plugin ID Pythonid) defines the APIs for PyCharm Professional.
The plugin Python Community Edition (https://plugins.jetbrains.com/plugin/7322-python-community-edition) (Plugin ID PythonCore) defines the APIs for PyCharm Community.
These plugins include the modules openapi and python-psi-api. These are considered stable APIs, but care should be taken to test your plugin with any version of PyCharm you wish to support.
Webinar Recording: "Live Development of a PyCharm Plugin" with Joachim Ansorg (https://blog.jetbrains.com/pycharm/2019/01/webinar-recording-live-development-of-a-pycharm-plugin-with-joachim-ansorg/)
When learning new development configurations, it is helpful to have some representative projects for reference:
Flake8 Support (https://github.com/jansorg/pycharm-flake8) Adds support for flake8's # noqa comments in PyCharm.
IDE: Homepage (https://www.jetbrains.com/rider), Versions (https://www.jetbrains.com/rider/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/rider)
Rider plugins are generally used to expose the functionality of a ReSharper (https://www.jetbrains.com/resharper/) plugin. Rider (https://www.jetbrains.com/rider/) uses the IntelliJ Platform somewhat differently ("Rider" in "The IntelliJ Platform") than other Platform-based based IDEs. Rider uses the IntelliJ Platform to provide the user interface for a C# and .NET IDE but uses ReSharper to provide the language-specific features.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
Although there is no dedicated Rider SDK, the ReSharper DevGuide (https://www.jetbrains.com/help/resharper/sdk/getting_started.html) addresses the subject of plugins for Rider. The documentation describes the configuration (https://www.jetbrains.com/help/resharper/sdk/creating_plugin.html) of the Gradle build script and settings.gradle file to build a Rider plugin using the Gradle project system (Configuring Gradle IntelliJ Plugin) in IntelliJ IDEA.
See Rider Extension Point and Listener List for the complete list.
Before starting a new Rider plugin project, review the article Writing plugins for ReSharper and Rider (https://blog.jetbrains.com/dotnet/2019/02/14/writing-plugins-resharper-rider/). In particular, this article discusses One Template to Rule Them All, a way to quickly get started writing plugins for both Rider and ReSharper.
More background information is available in the Building a .NET IDE with JetBrains Rider (https://www.codemag.com/Article/1811091/Building-a-.NET-IDE-with-JetBrains-Rider) article, which includes a high-level description of the Rider Protocol communication process between Rider and ReSharper. The article is a good counterpoint to the ReSharper DevGuide content, which discusses the protocol at the code level.
Rider plugins may introduce their own .DotSettings files with customized ReSharper settings (https://www.jetbrains.com/help/resharper/Sharing_Configuration_Options.html). This is useful e.g. when a plugin brings its own file templates.
For the ReSharper part to pick these settings, the settings files should be available in the plugin JAR file under the path dotnet/Extensions/$backend-plugin-id$/settings, where backend-plugin-id is calculated according to the following rules:
if the IntelliJ plugin id (the <id> ("id" in "Plugin Configuration File") element of the plugin.xml (Plugin Configuration File)) includes a dot, then backend-plugin-id is the same as the IntelliJ plugin id;
otherwise, the backend-plugin-id is a concatenation of the IntelliJ plugin vendor name (the <vendor> ("vendor" in "Plugin Configuration File") element of the plugin.xml) and the IntelliJ plugin id.
For example, for a plugin with the following plugin.xml contents, the file dotnet/Extensions/com.example.awesomeplugin/settings/templates.DotSettings would be picked up:
<idea-plugin>
<id>com.example.awesomeplugin</id>
<!-- ... -->
</idea-plugin>And the following plugin.xml would require placing the file under dotnet/Extensions/Jean-Luc Picard.Enterprise/settings/templates.DotSettings path:
<idea-plugin>
<id>Enterprise</id>
<vendor>Jean-Luc Picard</vendor>
<!-- ... -->
</idea-plugin>It can be useful to refer to existing projects to help understand how to build plugins for Rider. The following list of plugins is all open source, and can demonstrate how to implement different functionality. Please note that the list includes ReSharper plugins as well as Rider plugins. Since a lot of Rider's language features are shared with the ReSharper engine, and since the Rider SDK includes the ReSharper SDK, then it can be useful to look at ReSharper plugins too. Also note that these plugins might not be up-to-date with the current SDK.
Unity support for both ReSharper and Rider (https://github.com/JetBrains/resharper-unity)
F# support in JetBrains Rider (https://github.com/JetBrains/fsharp-support)
AgentMulder (https://github.com/ERNICommunity/AgentMulder/)
ConfigureAwaitChecker (https://github.com/aelij/ConfigureAwaitChecker/)
CognitiveComplexity (https://github.com/matkoch/resharper-cognitivecomplexity/)
CyclomaticComplexity (https://github.com/JetBrains/resharper-cyclomatic-complexity/)
Exceptional (https://github.com/CSharpAnalyzers/ExceptionalReSharper/)
HeapAllocationsViewer (https://github.com/citizenmatt/resharper-heapview/)
InternalsVisibleTo (https://github.com/hmemcpy/ReSharper.InternalsVisibleTo/)
PresentationAssistant (https://github.com/JetBrains/resharper-presentation-assistant/)
TestLinker (https://github.com/matkoch/TestLinker/)
Azure Toolkit for Rider (https://github.com/JetBrains/azure-tools-for-intellij)
T4 language support for both ReSharper and Rider (https://github.com/JetBrains/ForTea)
IDE: Homepage (https://www.jetbrains.com/ruby), Versions (https://www.jetbrains.com/ruby/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/ruby)
RubyMine (https://www.jetbrains.com/ruby/) is an IntelliJ Platform-based product. Plugin projects for RubyMine can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The configuration of RubyMine plugin projects follows the methods described in Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs"), and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs").
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the Gradle build script for a RubyMine plugin project. Click on an entry in the table's Attribute column to go to the documentation about that attribute. To see how these attributes appear in a similar Gradle build script for PhpStorm, see "Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs".
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IU for IntelliJ IDEA Ultimate. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | Set to the same IU BRANCH.BUILD as the RubyMine target version, e.g. 192.7142.36. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | org.jetbrains.plugins.ruby:2019.2.20191029 for the Ruby plugin. See below for Ruby plugin version information. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of RubyMine. For example, on macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/RubyMine/ch-0/192.7142.37/RubyMine.app/Contents. |
The required org.jetbrains.plugins.ruby plugin isn't compatible with IntelliJ IDEA Community edition but is compatible with IntelliJ IDEA Ultimate (IU) edition. Product compatibility is determined from the Ruby plugin version page (https://plugins.jetbrains.com/plugin/1293-ruby/versions). The Ruby plugin isn't bundled with IU, so the Ruby plugin version must be explicitly declared to support the target RubyMine (and IU) BRANCH.BUILD version. The correct Ruby plugin version is also determined from the Ruby plugin version page.
The dependency on the Ruby plugin APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") table, the <depends> ("depends" in "Plugin Configuration File") elements must contain com.intellij.modules.ruby. The dependency declaration is illustrated in the plugin.xml snippet below:
<!-- Requires the Ruby plugin -->
<depends>com.intellij.modules.ruby</depends>See RubyMine Extension Point and Listener List for the complete list.
Use the Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products") process to identify the library ruby.jar. Test your plugin with any version of RubyMine you intend to support.
When learning new APIs, it is helpful to have some representative projects for reference:
Ruby-Doc-Adder (https://github.com/aristotll/RubyDocAdder)
Ruby Dynamic Code Insight (https://github.com/JetBrains/ruby-type-inference)
Railways (https://github.com/basgren/railways)
IDE: Homepage (https://www.jetbrains.com/webstorm), Versions (https://www.jetbrains.com/webstorm/download/other.html)
Plugins: JetBrains Marketplace (https://plugins.jetbrains.com/webstorm)
WebStorm (https://www.jetbrains.com/webstorm/) is an IntelliJ Platform-based product. Plugin projects for WebStorm can be developed using IntelliJ IDEA with the Gradle IntelliJ Plugin.
Follow Building a Plugin for WebStorm – Tutorial for JavaScript Developers ("Articles" in "Learning Resources") blog post series to get started and How To Build a Plugin for JetBrains IDEs (Analog.js Example) ("Webinars" in "Learning Resources") webinar.
Qualifying Open Source projects can apply for free licenses (https://www.jetbrains.com/community/opensource/) of JetBrains products.
The configuration of WebStorm plugin projects follows the methods described in Configuring Plugin Projects using the IntelliJ IDEA Product Attribute ("Configuring Plugin Projects Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs") and Configuring the plugin.xml File ("Configuring plugin.xml" in "Plugins Targeting IntelliJ Platform-Based IDEs") for PhpStorm.
The table below summarizes the Gradle IntelliJ Plugin attributes to set in the plugin project's Gradle build script. Click on an entry in the table's Attribute column to go to the documentation about that attribute. To see how these attributes appear in a similar Gradle build script for PhpStorm, see "Configuring Gradle Build Script Using the IntelliJ IDEA Product Attribute" in "Plugins Targeting IntelliJ Platform-Based IDEs".
gradle-intellij-plugin Attribute | Attribute Value |
|---|---|
intellij.type ("type" in "Gradle IntelliJ Plugin") | IU for IntelliJ IDEA Ultimate. |
intellij.version ("version" in "Gradle IntelliJ Plugin") | 192.7142.36 Set to the same BRANCH.BUILD as the WebStorm target version. |
intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") | Dependency on the JavaScript plugin. |
runIde.ideDir ("ideDir" in "Gradle IntelliJ Plugin") | Path to locally installed target version of WebStorm. For example, for macOS: /Users/$USERNAME$/Library/Application Support/JetBrains/Toolbox/apps/WebStorm/ch-0/192.7142.35/WebStorm.app/Contents. |
The dependency on the WebStorm APIs must be declared in the plugin.xml (Plugin Configuration File) file. As described in Modules Specific to Functionality ("Modules Specific to Functionality" in "Plugin Compatibility with IntelliJ Platform Products") table, the <depends> ("depends" in "Plugin Configuration File") tags must declare JavaScript.
Note that for WebStorm, the plugin.xml file must also declare a dependency on com.intellij.modules.platform because JavaScript is not recognized as a module. Consequently, without the com.intellij.modules.platform declaration, the plugin is assumed to be a legacy plugin ("Declaring Plugin Dependencies" in "Plugin Compatibility with IntelliJ Platform Products") and will not load in WebStorm.
See WebStorm Extension Point and Listener List for the complete list.
See Exploring APIs as a Consumer ("Exploring APIs as a Consumer" in "Plugin Compatibility with IntelliJ Platform Products"). Test your plugin with any version of WebStorm you wish to support.
To use existing test base classes, specify com.jetbrains.intellij.javascript:javascript-test-framework:$VERSION$ as testImplementation dependency explicitly (see IntelliJ Platform Artifacts Repositories ("Gradle Example for an Individual Module from the IntelliJ Platform" in "IntelliJ Platform Artifacts Repositories")) (2020.3 and later).
When learning new plugin development, it is helpful to have some representative projects for reference:
Vue.js (https://github.com/JetBrains/intellij-plugins/tree/master/vuejs)
JS Toolbox (https://github.com/andresdominguez/jsToolbox)
Require.js (https://github.com/Fedott/WebStormRequireJsPlugin)
deep-js-completion (https://github.com/klesun/deep-js-completion)
Run Configuration for TypeScript (https://plugins.jetbrains.com/plugin/10841-run-configuration-for-typescript)
Beginning with the 2019.1 release, custom themes are supported. Custom themes give designers control of the appearance of built-in UI elements. The customization options include:
substitute icons,
change the colors of icons and UI controls,
alter the borders and insets of UI controls,
provide custom editor schemes,
add background images.
The themes available for download (https://plugins.jetbrains.com/search?headline=164-theme&tags=Theme) illustrate the creative possibilities.
See the Themes in IntelliJ-based IDEs (https://blog.jetbrains.com/platform/2021/10/themes-in-intellij-based-ides/) blog post for an overview (available in multiple languages).
Themes can be developed by using either IntelliJ IDEA Community Edition (https://www.jetbrains.com/idea/download/) or IntelliJ IDEA Ultimate (https://www.jetbrains.com/idea/download/) as your IDE (it is highly recommended to use the latest available version). Both include the complete set of development tools required to develop theme plugins. To become more familiar with IntelliJ IDEA, please refer to the IntelliJ IDEA Web Help (https://www.jetbrains.com/idea/help/).
A theme is one of the plugin types ("Themes" in "Plugin Types"). Its structure doesn't significantly differ from plugins extending IDE behavior, and can be implemented by using one of the supported approaches: DevKit or Gradle. The choice of the development approach depends on the project requirements and developer's experience.
Developing theme plugins with DevKit is the simplest solution and does not require experience with Gradle or similar build tools. The DevKit project structure is generated by default when an IDE Plugin theme project is created by using the New Project Wizard.
See the Developing a Theme section for the development instructions.
Developing theme plugins with Gradle requires experience with the Gradle or a similar build tool. It offers the possibility of automating some parts of the development process, like patching plugin.xml (Plugin Configuration File) file with the theme plugin version and other data, as well as building the plugin distribution on CI servers and publishing it to JetBrains Marketplace (https://plugins.jetbrains.com).
If your project requires any of the mentioned capabilities, see Developing a Plugin using Gradle (Developing a Plugin) for more details.
The recording of Busy Plugin Developer. Episode 3 shows how to create a new theme (https://youtu.be/9J0j-90dC60?t=582) using the Gradle approach.
This documentation section will help you get started with developing theme plugins for the IntelliJ Platform-based IDEs.
The DevKit-based theme development workflow includes:
Setting Up a Development Environment (Setting Up a Development Environment)
Creating a Theme Project (Creating a Theme Project)
Customizing a Theme (Customizing Themes - Icons and UI Controls)
Building and Running a Theme (Running and Debugging a Theme)
Deploying a Theme in IDE (Deploying a Theme)
Uploading a Theme to JetBrains Marketplace ("Uploading a Plugin to JetBrains Marketplace" in "Publishing a Plugin")
Plugin DevKit plugin is bundled with IntelliJ IDEA until 2023.2.
Plugin DevKit AvailabilityWhen using IntelliJ IDEA 2023.3 or later, the Plugin DevKit plugin must be installed from JetBrains Marketplace (Plugin Homepage (https://plugins.jetbrains.com/plugin/22851-plugin-devkit)) as it is no longer bundled with the IDE.
Use the following checklist to ensure that you are ready to develop your custom theme:
IntelliJ IDEA Community Edition (https://www.jetbrains.com/idea/download/) or IntelliJ IDEA Ultimate (https://www.jetbrains.com/idea/download/) is installed.
Plugin DevKit plugin is installed and enabled in IntelliJ IDEA (https://www.jetbrains.com/help/idea/managing-plugins.html).
IntelliJ IDEA CE source code is checked-out. (Optional)
This step is needed only when you plan to debug the IntelliJ Platform code. See for more details.
IntelliJ Platform SDK is configured.
Getting the IntelliJ IDEA CE source code is not a requirement for theme development as debugging the platform code while developing a theme is a rare situation. In case you are developing a plugin extending IDE behavior, or you need to understand how some components work internally, having sources makes debugging much more straightforward.
For detailed instructions on how to check out the code efficiently, refer to the Getting IntelliJ IDEA Community Edition Source Code section of IntelliJ IDEA Community Edition README file (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/README.md). Note that building the checked-out sources is not required in this case.
For more information about SDKs, see SDKs section (https://www.jetbrains.com/help/idea/working-with-sdks.html) in the IntelliJ IDEA Web Help.
To see the effects of the developed plugin in real IDE, it is required to run the plugin in an IDE Development Instance. In most cases, it is enough to download and use IntelliJ IDEA (https://www.jetbrains.com/idea/download/) Community Edition. If it is required to style components used only in a specific IDE like IntelliJ IDEA Ultimate or WebStorm, they can also be used as SDK, but debugging the core code will only work with the IntelliJ IDEA Community Edition.
The first step of configuring a theme plugin SDK is adding the JDK.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Go to File | Project Structure | Platform Settings | SDKs.
Click the Add button (+).
If you have the required JDK installation on your machine, and it is detected, select it from the Detected SDKs list. If your JDK is not detected, select the Add JDK... option and choose the installation folder.
If the required JDK is not installed on your machine, the simplest option is using Download JDK... and choosing the distribution options.
Click the Apply button.
The second step is adding IntelliJ Platform Plugin SDK that will use the JDK configured in the first step.
Go to File | Project Structure | Platform Settings | SDKs.
Click the Add button (+).
Select the Add IntelliJ Platform Plugin SDK... option.
Choose the installation folder of the IDE downloaded previously (on macOS, select application icon in /Applications/).
In the Select Internal Java Platform dialog, select the JDK configured in the previous step and click OK button.
In the added SDK, specify the Sandbox Home directory.
See "The Development Instance Sandbox Directory" in "IDE Development Instance" for details.
If debugging is required, select the Sourcepath tab, click the Add button (+) and select the root folder of the checked-out sources.
Click the Apply button.
This documentation page describes a DevKit-based theme project generated with the New Project Wizard (https://www.jetbrains.com/help/idea/new-project-wizard.html).
Before creating a theme project, make sure that development environment is set up (Setting Up a Development Environment).
Launch the New Project wizard via the File | New | Project... action and provide the following information:
Select the IDE Plugin generator type from the list on the left.
Specify the project Name and Location.
Choose the Theme option in the project Type.
As the JDK select the configured SDK ("Configuring IntelliJ Platform Plugin SDK" in "Setting Up a Development Environment").
Click the Create button to generate the project.
For the example my_theme created with the steps describes above, the IDE Plugin generator creates the following directory content:
META-INF directory with:
plugin.xml (Plugin Configuration File) configuration file containing preconfigured theme provider
pluginIcon.svg file that is a plugin logo (Plugin Logo)
Replace it with a custom logo representing the Theme.
my_theme.theme.json - a minimal theme description file ("Introduction to Theme Description File Syntax" in "Customizing Themes - Icons and UI Controls")
my_theme.iml - IntelliJ IDEA Module (https://www.jetbrains.com/help/idea/creating-and-managing-modules.html) configuration file
See Theme Structure for more details.
Theme plugin project created with the new project wizard contains a single theme description file by default. In case a plugin needs to provide multiple themes (e.g., dark and light variants), it is possible to add them with the dedicated action.
Additional plugin themes can be added using the DevKit Theme Wizard, which is a part of the Plugin DevKit plugin. The wizard can be used for both DevKit-based and Gradle-based plugins.
In the Project tool window, select the resources directory and invoke the context menu with the right click.
Select the New | Plugin DevKit | Theme action.
It is a good practice to include the name of the plugin in the created theme name.
Check the Dark theme checkbox if the created theme should be based on IntelliJ IDEA Darcula theme. Otherwise, the Light theme will be used as the base.
Click OK button to generate the theme description file ("Introduction to Theme Description File Syntax" in "Customizing Themes - Icons and UI Controls") with $THEME_NAME$.theme.json name. The generated theme will be automatically configured in the plugin.xml file.
In most cases, the results of theme plugin can be previewed by running the Preview Theme action in the theme description file (Customizing Themes - Icons and UI Controls) editor. Sometimes, when more advanced styling options are implemented, the previewing theme may not be enough to see all the changes. This page explains how to run or debug the theme plugin in the IDE configured as a part of project SDK (Setting Up a Development Environment).
To run and debug a plugin directly from a theme plugin project, a Plugin run configuration must be added.
For more information about creating Run Configurations, refer to the Run/Debug Configuration (https://www.jetbrains.com/help/idea/run-debug-configuration.html) section in IntelliJ IDEA Web Help.
Go to Run | Edit Configurations....
Click the Add New Configuration... button (+) and select the Plugin type.
Provide the configuration Name, e.g., Run Theme.
Ensure that Use classpath of module specifies the current theme plugin module.
Click the Apply button.
See the IDE Development Instance section for information about advanced run configuration settings. If additional settings, like system property, are needed, see Run/Debug Configuration: Plugin (https://www.jetbrains.com/idea/help/run-debug-configuration-plugin.html) explaining how to configure them.
To run the theme in the IDE development instance, choose Run | Run... and select the created run configuration. Debugging a theme is similar, but instead of Run..., select the Debug... action.
Before your custom theme plugin can be uploaded to JetBrains Marketplace ("Uploading a Plugin to JetBrains Marketplace" in "Publishing a Plugin") and used by users, it should be packaged and verified in the actual IDE.
The deployment process prepares the plugin artifact that can be installed in IDE.
Build the theme by invoking Build | Build Project or Build | Build Module $MODULE_NAME$.
Create the deployment artifact by invoking Build | Prepare Plugin Module $MODULE_NAME$ for Deployment.
The resulting theme JAR file will be created in the project or module directory.
In the case of developing a regular plugin, and it specifies additional dependencies, a ZIP archive is created, including all the plugin libraries.
Install (https://www.jetbrains.com/help/idea/managing-plugins.html#installing-plugins-from-disk) the newly created JAR or ZIP file from disk.
Click the Apply button.
Select your theme in Settings | Appearance & Behavior | Appearance and apply the changes.
Themes are components within IntelliJ Platform plugins (Plugin Structure). The theme plugins should be stand-alone and not combined with other plugin functionality.
To see a full example theme project, see the Theme Basics (https://github.com/JetBrains/intellij-sdk-code-samples/tree/main/theme_basics) in IntelliJ SDK Code Samples.
Themes have several components:
A required Theme description (JSON) file in the plugin project's resources folder.
A required themeProvider declaration in the plugin's plugin.xml (Plugin Configuration File) file, located in the plugin project's META-INF folder.
An optional Editor Scheme description (XML) file derived from an exported IDE editor scheme. This file is located in the plugin project's resources folder.
An optional background image file, located in the plugin project's resources folder.
Optional icon image files, located in the plugin project's resources folder.

The most important file in every theme project is the theme description file. The content of the default file generated with the Theme wizard (Creating a Theme Project) is a short set of key–value pairs:
{
"name": "theme_basics",
"author": "",
"dark": false,
"editorScheme": "/theme_basics.xml",
"ui": {
}
}name key matches the first portion of the Theme description $THEME_NAME$.theme.json file name. The value of name is displayed in the Theme (https://www.jetbrains.com/help/idea/settings-appearance.html) Settings dropdown when the theme's plugin is installed in the IDE.
author - specifies the theme author (empty by default).
dark - determines the base theme (Light or Darcula) that is customized.
editorScheme - specifies the editor scheme file that describes fonts and colors used in editors (see "Adding a Custom Editor Scheme" in "Themes - Editor Schemes and Background Images" for more details).
ui - allows for overriding the base theme (Light or Darcula) properties (see "Custom UI Control Colors" in "Customizing Themes - Icons and UI Controls" for more details).
The wizard also creates a themeProvider declaration in the <extensions> ("extensions" in "Plugin Configuration File") section of the plugin's plugin.xml file. This declaration binds the theme description file to a theme provider extension using a generated unique id.
<extensions defaultExtensionNs="com.intellij">
<themeProvider
id="eb9b7461-397b-4b98-a422-224fc0a74564"
path="/theme_basics.theme.json"/>
</extensions>Do not modify or re-use an existing value of the generated id attribute.
The following sections describe the theme customization possibilities in detail:
"Customizing Icons" in "Customizing Themes - Icons and UI Controls"
"Customizing UI Controls" in "Customizing Themes - Icons and UI Controls"
"Adding a Custom Editor Scheme" in "Themes - Editor Schemes and Background Images"
"Adding a Custom Background Image" in "Themes - Editor Schemes and Background Images"
Platform UI Guidelines: Icons (https://jetbrains.design/intellij/principles/icons/), Platform theme colors (https://jetbrains.design/intellij/principles/platform_theme_colors/#UI-components)
A theme is customized by adding information to the theme description file that overrides the base (Light or Darcula) theme.
For plugin developers, Exposing Theme Metadata (Exposing Theme Metadata) discusses the format of customization keys and information about how to provide it to Theme authors.
Color Highlighter Plugin (https://plugins.jetbrains.com/plugin/13309-color-highlighter) adds additional color preview inside the editor.
The syntax of a Theme description file follows the JSON open-standard file format of key-value pairs. The minimum valid default file is the output of the DevKit Theme Wizard (Creating a Theme Project). Adding key-value pairs customizes a Theme.
Theme key-value pairs often use color as the value. Colors are defined by six-digit RGB or eight-digit RGBA hexadecimal notation.
When learning new syntax, it is often useful to have some existing implementations for reference. For example, refer to the Theme description file (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/HighContrast.theme.json) for the IntelliJ IDEA High Contrast Theme. It may also help to review themes available (https://plugins.jetbrains.com/search?headline=164-theme&tags=Theme) at the JetBrains Marketplace.
Colors can always be defined individually as six-digit RGB or eight-digit RGBA hexadecimal notation. However, Theme definitions often use the same color in multiple places. Maintaining a Theme is more manageable if Named Colors are globally defined in a colors {} block as part of the *.theme.json file. After that, the Named Color can be used instead of a hexadecimal description of the color. For example, defining the Named Color basicBackground and then using it to set the background color of panels. (Don't be concerned with the "ui" syntax in the example below; it will be discussed in Custom UI Control Colors.)
{
"name": "theme_basics",
"dark": false,
"author": "IntelliJ Platform SDK",
"editorScheme": "/Lightning.xml",
"colors": {
"basicBackground": "#E1E1E1"
},
"ui": {
"Panel.background": "basicBackground"
}
}Themes can customize the color of default IntelliJ Platform UI icons or substitute custom icons for the default ones. Customization is done by adding an "icons": {} section to the Theme description file.
Default global icon colors are customized by adding key-value pairs to a "ColorPalette": {} section. The ColorPalette must be inserted in the icons section.
In the following example, the key - the default red color (#DB5860) used for Action icons in the Light Theme - is overridden to the value of a different color (#D61A26):
{
"icons": {
"ColorPalette": {
"#DB5860": "#D61A26"
}
}
}This color substitution is applied throughout the IDE UI.
Icon Palettes are predefined theme color keys that describe a single color in an Actions or Objects context.
IntelliJ Platform has default icon colors defined for Actions and Objects contexts.
Actions are Theme keys for icons that appear in the context of toolbars, and represent actions such as Compile, Run, or Debug.
Objects are Theme keys for icons that appear in the contexts of lists and trees, and represent entities like files, symbols, or run and debug configurations.
The JetBrains Platform UI Guideline for Icons (https://jetbrains.design/intellij/principles/icons/) defines the default hexadecimal RGB values of colors for Actions and Objects keys. Note that this document refers to Objects keys as "Noun icons."
An icon Palette color is customized by adding an Actions or Objects key and custom color value to the "ColorPalette": {} section in a Theme description file. The list of available icon Actions and Objects keys are provided by the editor's completion popup:

For example, the following key-value pair changes the color for all blue-colored icons on toolbars to the color #5BC0DE:
{
"icons": {
"ColorPalette": {
"Actions.Blue": "#5BC0DE"
}
}
}This more specific change to the Actions.Blue color overrides the default definition. In the narrower context of blue Actions icons, it will also supersede any global color overrides of the default blue icon color.
The default IntelliJ Platform UI icons can be replaced by custom icons. The file format for icons is SVG. The JetBrains Platform UI Guideline for Icons (https://jetbrains.design/intellij/principles/icons/) has detailed specifications for icons.
An icon replacement is described within the icon {} section of a Theme description file. Note that icon replacement key-value pairs appear outside the ColorPalette section.
For icon substitutions, the key is the path to the default icon image. This path is derived from the AllIcons.[Group].[IconName] path in icon section reported by the UI Inspector (Internal Actions - UI Inspector).
For example, the Build (hammer) icon in the toolbar has the path Allcons.Actions.Compile as reported by the UI Inspector. Therefore, the key for the Build icon is /actions/compile.svg. The value is the replacement icon's file name, located in the resources folder of the theme plugin project:
{
"icons": {
"/actions/compile.svg": "/factory.svg"
}
}The color of a replaced icon takes precedence over any ColorPalette overrides.
For generating the SVG icons suited for the IntelliJ-based IDEs, you may also use the third-party web tool: IntelliJ Icon Generator (https://bjansen.github.io/intellij-icon-generator/).
Themes can change the appearance of more general controls in the IntelliJ Platform UI. Examples of these controls are labels, buttons, checkboxes, trees, lists, and menus.
See also Platform theme colors — UI components (https://jetbrains.design/intellij/principles/platform_theme_colors/#UI-components) in IntelliJ Platform UI Guidelines.
A UI control's custom color is specified by adding a key-value pair to the "ui": {} section of a Theme description file.
A UI control key has the compound format element.property, where:
element is the type (label, checkbox, etc.) of the UI control.
property is how the color is applied. Examples include foreground, background, and errorForeground.
Note that some UI control keys have more than two parts, for example, Popup.Advertiser.foreground or ScrollBar.Mac.Transparent.thumbColor. The full key must be used to customize that specific button control. However, for other purposes, the first section can be considered the element, and the last section considered the property.
Methods for finding UI control keys are in the Finding Attribute Keys for UI Controls section.
All UI Controls with the same property portion of their key can be set to the same color. This customization is done using the wildcard "*": {} section in the Theme description file. A key-value pair is inserted in this section, but only the property portion of the key is specified. The value is the custom color.
The following example would change the default background color to #AED7E3 for all UI controls:
{
"ui": {
"*": {
"background": "#AED7E3"
}
}
}Note that the wildcard "*": {} section must be within the "ui": {} section.
The color of a specific UI control type is changed by adding a key-value pair to the "ui": {} section of a Theme description file. The key is the full element.property format, and the value is the custom color.
The following example sets the background color for all labels to the color #F6E9C9.
{
"ui": {
"*": {
"background": "#AED7E3"
},
"Label.background": "#F6E9C9"
}
}The Label.background entry supersedes, in the narrower context of label backgrounds, any default color, and any wildcard color assigned to backgrounds.
UI Tab colors are changed by key-value pairs in a Theme description file.
There are two implementations of tabs in the IntelliJ Platform:
Editor Tabs, which represent open files in the Editor window (https://www.jetbrains.com/help/idea/using-code-editor.html), and in Tool Window bars (https://www.jetbrains.com/help/idea/tool-windows.html#bars_and_buttons).
Tabbed Panes, which are used for the Run/Debug Configurations dialog (https://www.jetbrains.com/help/idea/run-debug-configurations-dialog.html).
The control keys for UI Tabs were expanded from release 2019.1 to 2019.2 of the IntelliJ Platform. The 2019.1 release control keys are compatible with release 2019.2 and later versions of the IntelliJ Platform.
Release 2019.1 Element | Release 2019.2 Element | Description of Release 2019.2 Element |
|---|---|---|
N/A | DefaultTabs | Applied to all tabs except TabbedPane, unless overridden by a more specific Tab control element. |
EditorTabs | EditorTabs | Applied only to Editor tabs. Overrides any DefaultTab settings. 192 has many more property settings than 191. |
ToolWindow.HeaderTab | ToolWindow.HeaderTab | Applied only to Tool Window tabs. Overrides any DefaultTab settings. 192 has many more property settings than 191. |
DebuggerTabs | DefaultTabs | DefaultTab settings are used instead of DebuggerTabs, except for key DebuggerTabs.underlineHeight. |
TabbedPane | TabbedPane | Applied only to Tabbed Panes. |
Plugins.Tab | TabbedPane | Use TabbedPane instead. |
SearchEverywhere.Tab | SearchEverywhere.Tab | No change. |
Methods for identifying UI control keys are in the Finding Attribute Keys for UI Controls section.
For example, here is an excerpt from the IntelliJ Platform High Contrast Theme (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/HighContrast.theme.json): Note that a Theme file can mix versions of property identifiers:
The first three property entries are recognized by release 2019.1 and ignored by subsequent releases because they are defined by new property identifiers.
The underlineColor property is recognized by release 2019.1 and subsequent releases.
The underlineHeight property was introduced in release 2019.2, and is ignored by previous releases.
The underlinedTabBackground property was introduced in release 2019.2, replaces the 2019.1 selectedBackground, and is ignored by previous releases.
The inactiveColoredFileBackground property was introduced in release 2019.2, replaces the 2019.1 inactiveMaskColor, and is ignored by previous releases.
{
"ui": {
"EditorTabs": {
"selectedForeground": "#FFFFFF",
"selectedBackground": "#0e5d73",
"inactiveMaskColor": "#000000FF",
"underlineColor": "#1AEBFF",
"underlineHeight": 4,
"underlinedTabBackground": "#000000",
"inactiveColoredFileBackground": "#00000000"
}
}
}The color and geometry of borders used in UI controls can be customized by key-value pairs in a Theme description file. The format of keys for borders is element.property, where:
element is the type of UI control containing a border, such as a window or a popup menu.
property is the desired border appearance, for example:
border is the border width (in pixels) specified as a top, left, bottom, and right widths. Border color is also (optionally) specified as hexadecimal RGB, e.g. E6E6E6 with no # character.
borderInsets is the inset distance (in pixels) from the edge of the element to the border. It is specified as top, left, bottom, and right insets.
Methods for identifying UI control keys are in the Finding Attribute Keys for UI Controls section.
Adding a key-value pair to the "ui": {} section of a Theme description file customizes the appearance of borders for specific UI control types.
The following example sets a new border width and color for all windows:
{
"ui": {
"Window.border" : "4,4,4,4,E6E6E6"
}
}In this example, the customized border supersedes the default definition and any global color override.
There are hundreds of UI control element.property keys defined in the IntelliJ Platform UI. Some keys and strategies for applying them can be gleaned from the theme reference implementations. For a general search, here some suggested methods for locating UI control keys.
The preferred method of finding UI control keys is to use the Code Completion (https://www.jetbrains.com/help/idea/auto-completing-code.html#Auto-Completing_Code.xml) feature in the editor. Note that some keys presented by the code completion feature may be deprecated. New entries in the "ui": {} section will invoke the code completion popup, as shown below:

Beginning with version 2019.2 of the IntelliJ Platform, the editor has added features for Code Completion and Quick Documentation (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation) to show the release in which a UI control key began to be supported. It appears as the Since attribute in editor popups. In the Quick Documentation popup, the format is e.g., Since: 2019.2. The Code Completion popup is similar, but the format is e.g., [Since 2019.2].
Using the Laf Defaults (Internal Actions - LaF Defaults) inspector, enter the element portion of the key. The Laf Defaults inspector will prompt with a list of UI Control keys and their default color.
Themes can also provide custom color and font settings, as well as custom images for display in the IDE application window.
Users of IntelliJ Platform-based IDEs, such as IntelliJ IDEA, can set preferences to configure the colors and fonts used in the Editor. These custom color and font settings are called Editor Color Schemes.
Custom editor color schemes can be specified and exported using the IDE Settings dialog. Note that editor Colors and Fonts (https://www.jetbrains.com/help/idea/configuring-colors-and-fonts.html), and Colors for Version Control File Status (https://www.jetbrains.com/help/idea/file-status-highlights.html) are customized in different sections of Settings.
Use the following procedure to customize an editor color scheme for a theme:
Create the desired custom editor color scheme using the IDE preferences.
Export the custom editor color scheme to the desired file name. In this example, the file is exported to Lightning.icls.
Once exported, change the file extension from *.icls to *.xml. In this example, the result is Lightning.xml.
See Customizing Editor Scroll Bar Colors to change the colors of editor scroll bars.
The next step is to add the color scheme to the theme plugin project:
Replace the default generated custom editor color scheme XML file (in this example, theme_basics.xml) in the project's resources folder with the exported custom editor color scheme. In this case, the action is to replace theme_basics.xml with Lightning.xml.
In the theme file (in this example theme_basics.theme.json), replace the name of the generated editor scheme file (theme_basics.xml) with the new (Lightning.xml) file name. The key is always "editorScheme". The value is the name of the editor color scheme file.
The example below adds an editor scheme named "Lightning" to the Theme Basics custom theme:
{
"name": "Theme Basics",
"dark": false,
"author": "IntelliJ Platform SDK",
"editorScheme": "/Lightning.xml",
"ui": {
}
}When an editor color scheme is exported as a file, the color options appear as name-value attributes of option elements. The name is the aspect of the editor to be changed, and the value is the new color in six-digit RGB or eight-digit RGBA hexadecimal notation. For example, the snippet below sets the color of the line numbers displayed in the editor:
<colors>
<option name="LINE_NUMBERS_COLOR" value="999999"/>
</colors>For additional examples of name and value attributes, review the editor color scheme XML file for the High Contrast editor scheme (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/highContrastScheme.xml).
As described above, colors corresponding to the VCS status of files can be customized and exported via the Settings (https://www.jetbrains.com/help/idea/file-status-highlights.html). No other procedure is necessary to customize these colors. In the exported color scheme file the name is the VCS file status, and the value is the new color corresponding to that status. For example, customized VCS colors for a subset of file statuses will appear in the editor scheme file as:
<colors>
<option name="FILESTATUS_ADDED" value="62cc47"/>
<option name="FILESTATUS_COPIED" value="62cc47"/>
<option name="FILESTATUS_DELETED" value="ed864a"/>
</colors>For additional examples of FILESTATUS color name attributes, see the editor color scheme XML file for the High Contrast editor scheme (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/highContrastScheme.xml).
Editor scroll bar colors should be coordinated with, and switch together with an editor color scheme. Please note that custom theme (*.theme.json) files also contain ScrollBar.* name attributes, but these are for scroll bars outside the context of the editor.
The Editor Scroll Bar colors are the only editor scheme settings that cannot be customized and exported through IDE preferences.
Customizing the editor scroll bar colors requires manually changing an editor color scheme XML file. At this time there isn't code completion functionality for changing custom color editor scheme XML files, so the name attributes are described below.
The typical format of a scroll bar name attribute is ScrollBar.usage, where usage describes where the color is to be applied. In some cases usage itself can be compound such as ScrollBar.Mac.Transparent.thumbColor. In these compound cases, the last portion of the compound usage still describes where the color is to be applied.
Note that the following example snippet uses an eight-digit hexadecimal color value to give ScrollBar.Mac.thumbColor transparency:
<color>
<option name="ScrollBar.Mac.trackColor" value="000000"/>
<option name="ScrollBar.Mac.thumbColor" value="FFFFFFBE"/>
</color>A list of scroll bar name attributes is in the High Contrast editor scheme (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/themes/highContrastScheme.xml) file. These name attributes cannot be accessed from anywhere in the IDE UI at this time, so they must be manually added to an editor color scheme XML file.
The following list explains the usage format of the name attribute, i.e. where a custom scroll bar color is applied:
*.trackColor - The scroll bar thumb moves across this area. At this time the vertical scrollbar track color and transparency cannot be customized.
*.thumbColor - The movable rectangle that corresponds to the visible content's size.
*.thumbBorderColor - The thumb border.
*.hoverTrackColor - Same usage as above but for hover. At this time the vertical scrollbar hover track color and transparency cannot be customized.
*.hoverThumbColor - Same usage as above but for hover.
*.hoverThumbBorderColor - Same usage as above but for hover.
The name attribute patterns are enumerated below.
Platform Independent Name Attributes
The horizontal scroll bar background color is set by ScrollBar.background. This background color is visible only if the horizontal scroll bar's *.trackColor has transparency.
At this time, the vertical scrollbar background color cannot be customized.
Windows/Linux Name Attributes
The name attributes for Windows and Linux scroll bars have the pattern ScrollBar.Transparent.*, where the wildcard portion corresponds to the usage definitions above.
macOS Name Attributes
The name attribute pattern for the vertical scroll bar is ScrollBar.Mac.*.
The name attribute pattern for the horizontal scroll bar depends on the macOS preferences Show scroll bars setting:
ScrollBar.Mac.* when the Always setting is selected.
ScrollBar.Mac.Transparent.* when the When scrolling setting is selected.
The wildcard portion of these patterns corresponds to the usage definitions above.
The IDE supports setting an image as a background in the application window. Users can do this manually in Settings (https://www.jetbrains.com/help/idea/setting-background-image.html).
Themes support specifying a background image as a key-value pair in the "background": {} (for editor and tools) and "emptyFrameBackground": {} (for empty frame) sections of a Theme description file:
The image key uses the file name of the image as the value. The background image is placed in the theme plugin project's resources folder.
The transparency key uses a value of 1-100. A value of 100 is opaque.
The fill key uses a value of scale, meaning to expand the image to fill the space as the window gets resized.
The anchor key uses a value of center, meaning to locate the center of the image in the center of the window.
The following example adds an image of the Austrian countryside to the Theme Basics Theme description file:
{
"name": "Theme Basics",
"dark": false,
"author": "IntelliJ Platform SDK",
"ui": {
},
"background": {
"image": "/austria.png",
"transparency": 10,
"fill": "scale",
"anchor": "center"
},
"emptyFrameBackground": {
"image": "/austria.png",
"transparency": 20,
"fill": "scale",
"anchor": "center"
}
}The emptyFrameBackground property is available starting with the 2020.2 release.
See also Oracle's Java Technology Glossary (https://www.oracle.com/java/technologies/glossary.html).
The Abstract Syntax Tree (Implementing Parser and PSI) represents the structure of source input files. → Program Structure Interface
Provides semantic highlighting (Syntax and Error Highlighting) based on underlying → Program Structure Interface elements. → Inspection
Executing in the blocking context ("Blocking Context" in "Execution Contexts") means executing tasks on a thread without access to a coroutine context. → Suspending Context → Coroutine
java.util.concurrent.CancellationException, kotlin.coroutines.cancellation.CancellationException (typealias in stdlib), kotlinx.coroutines.CancellationException (typealias in kotlinx-coroutines) → ProcessCanceledException
A Kotlin coroutines (Kotlin Coroutines) execution unit allowing for handling concurrency and asynchronous tasks efficiently with a sequential/imperative code style.
Part of the coroutine context. Determines a thread or a thread pool the corresponding coroutine is executed on. See Coroutine Dispatchers for more details. → Coroutine
Coroutine scopes (Coroutine Scopes) define the lifetime of coroutines and ensure proper handling of coroutine cancellations and structured concurrency. → Coroutine
XML DOM API abstracts working with XML files based on a custom semantic model.
The Event Dispatch Thread (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html) handles all Swing events. See also General Threading Rules and "EDT Dispatcher" in "Coroutine Dispatchers".
Most functionality is provided by Using Extension Points (Extensions) provided by the platform or plugins. Plugins can also define their own (Extension Points) to allow extensibility.
External System Integration allows integrating external project management systems.
JetBrains internal API to track feature usage in the IDE.
File Based Index (File-Based Indexes) allows storing key-value information based on the project's files.
A configuration file written in Kotlin (build.gradle.kts) or Groovy (build.gradle) that describes the build process and dependencies of a plugin.
Supports configurable semantic highlighting (Code Inspections and Intentions). → Annotator
Provides an automated fix (Code Inspections and Intentions) for commonly encountered code problems.
Communication standard between development tools and Language Servers, see Language Server Protocol (LSP).
A builtin → Version Control System tracking all changes in the project locally (https://www.jetbrains.com/help/idea/local-history.html).
Defines the visual appearance and behavior of the user interface; see Swing Tutorial (https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/index.html).
Represents the project model in External Build ("Accessing Project Model and Configuration from External Build" in "External Builder API and Plugins") process.
The JetBrains Runtime ("Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance") is the default bundled runtime for all IntelliJ Platform-based IDEs by JetBrains.
A → Read Action that is canceled by a → Write Action. See also "Read Action Cancellability" in "General Threading Rules".
ProcessCanceledException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/base/src/com/intellij/openapi/progress/ProcessCanceledException.java) An exception indicating that the currently running operation was terminated and should finish as soon as possible. → CancellationException
The Program Structure Interface (Program Structure Interface (PSI)) represents a syntactic and semantic code model of the source input files. → Abstract Syntax Tree → Stubs
Allows accessing code-related data structures for reading purposes. See also General Threading Rules. → Non-Blocking Read Action → Write Action
A Run Configuration (Run Configurations) allows running external processes from within the IDE.
Allows searching and replacing code by defining the structure of the searched code fragments, see "Structural Search and Replace Inspections" in "Alternatives to Implementing a Plugin".
A subset of a → Program Structure Interface tree in a binary serialized compact format, see Stub Indexes.
Executing in the suspending context ("Suspending Context" in "Execution Contexts") means executing tasks in Kotlin coroutines. → Blocking Context → Coroutine
A semantic element in some model, e.g., language or framework model, see Symbols.
An abstraction layer (UAST - Unified Abstract Syntax Tree) on the → Program Structure Interface of different JVM languages.
The API for Version Control System (Version Control Systems) allows accessing builtin as well as adding custom implementations.
A Virtual File (Virtual Files) represents a file in a → Virtual File System.
A Virtual File System (Virtual File System) provides a unified API for working with files represented as → Virtual File.
Allows accessing code-related data structures for writing purposes. See also General Threading Rules. → Read Action
A coroutine → Read Action that is canceled by an incoming → Write Action. See "Coroutine Read Actions API" in "Coroutine Read Actions" for details. → Suspending Context → Coroutine
A coroutine → Read Action that blocks incoming → Write Action. See "Coroutine Read Actions API" in "Coroutine Read Actions" for details. → Suspending Context → Coroutine
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Sometimes it can be challenging to implement plugin features for the IntelliJ Platform, especially when you've hit a roadblock, and you're unsure how to move forward. This usually happens in two situations:
You're trying to implement a feature that you've already seen in the IDE, and now you need to find the appropriate extension point or class that allows you to hook into the relevant mechanisms.
You've already started working on a feature, but you're unsure how the different parts of the IntelliJ Platform interact with each other. In such situations, it is helpful to be able to navigate the IntelliJ Platform code confidently and to find relevant examples in other plugins.
This guide provides a list of proven strategies that can help you overcome these challenges and gather enough information to continue your work. Furthermore, the tips below will help build your confidence as you explore the IntelliJ Platform.
Plugin AlternativesIn some cases, implementing an actual IntelliJ Platform plugin might not be necessary, as alternative solutions (Alternatives to Implementing a Plugin) exist.
See also Required Experience about necessary technology knowledge.
The most important resource for discovering new EPs is the extensive list provided directly in the IntelliJ Platform SDK Documentation (IntelliJ Platform Extension Point and Listener List). On this page, you will find all the EPs, and each entry includes a link to the online source code and a link to the IntelliJ Platform Explorer (https://jb.gg/ipe), which helps you find examples of this EP in other plugins. Additionally, dedicated Extension Point Lists specific to IDEs are available under Product Specific.
Another way to discover EPs is by using autocompletion or navigating through EP XML files. When you open a new tag in your plugin.xml (Plugin Configuration File) file (inside the <extensions> ("extensions" in "Plugin Configuration File") block with defaultExtensionNs="com.intellij"), the IDE will automatically suggest possible EPs.
This is the first step in discovering new features that haven't been explicitly mentioned in the IntelliJ Platform Docs. Note that in the completion popup, you can call quick documentation (https://www.jetbrains.com/help/idea/viewing-reference-information.html#inline-quick-documentation), which then shows its properties, the implementation class, as well as a direct link to open usage results from IntelliJ Platform Explorer (https://jb.gg/ipe).
Use Go to Declaration (https://www.jetbrains.com/help/idea/navigating-through-the-source-code.html#go_to_declaration) on EPs that are implemented in plugin.xml to navigate to its definition in the XML file. There you'll find more EPs, and browsing through this list helps you discover features you might not have been aware of. Search everywhere (https://www.jetbrains.com/help/idea/searching-everywhere.html) or Go to file (https://www.jetbrains.com/help/idea/discover-intellij-idea.html#navigation-and-search) helps you search for all files containing extension points. Just use *ExtensionPoints.xml as the search pattern and select the All Places scope.
However, if a bundled or third-party plugin exposes EPs for others to implement, these EPs are defined in the plugin.xml files of the plugins and not in the *ExtensionPoints.xml files of the IntelliJ Platform. One such example is the EPs exposed by the Markdown plugin that adds support for custom languages inside fenced code blocks of Markdown files.
Explore the plugin.xml files of bundled or 3rd party plugins. If you have the IntelliJ Platform sources available either in your own plugin project or in a separate instance, you can use Structural Search (https://www.jetbrains.com/help/idea/structural-search-and-replace.html) to find all the files that meet the following criteria:
The file type is XML
It contains the tag <idea-plugin> ("idea-plugin" in "Plugin Configuration File")
The file is in the scope Project and Libraries

The search results will contain many plugin XML files. To find specific implementations of EPs in third-party plugins, use the IntelliJ Platform Explorer (see 3.2 ("3.2 Search the IntelliJ Platform Explorer" in "Explore the IntelliJ Platform API")). Inspecting the plugin.xml files of other plugins not only helps you discover new features but also shows how things like menu entries or notification groups can be defined in the XML file.
The following tips will help you navigate through the IntelliJ Platform source code if you already have an idea of what you're looking for. It's important that you're familiar with navigating (https://www.jetbrains.com/help/idea/reference-keymap-win-default.html#navigate_from_symbols) and searching (https://www.jetbrains.com/help/idea/reference-keymap-win-default.html#find_everything) source code, as well as other basic features of IntelliJ IDEA.
Many developers keep the IntelliJ Community source code (https://github.com/JetBrains/intellij-community) open in a separate window while working on their plugin. Others simply search the source code of the IntelliJ Platform that is attached by default when using a Gradle IntelliJ Plugin (Gradle IntelliJ Plugin)-based project. While both methods work, it should be noted that developing plugins without inspecting the IntelliJ Platform code is nearly impossible, and all the tips below assume that you have the source code available.
When working with interfaces or abstract classes of EPs, use IntelliJ IDEA's Go to Implementation (https://www.jetbrains.com/help/idea/navigating-through-the-source-code.html#go_to_implementation) or Find Usages (https://www.jetbrains.com/help/idea/find-usages-dialog.html) feature to discover examples of how they are used in the IntelliJ Platform.
Access to many features is provided through the Manager and Service classes, such as:
com.intellij.openapi.application.ApplicationManager
com.intellij.psi.PsiManager
Therefore, it can be helpful to search for classes that match the pattern com.intellij.*Manager and look through the list of results. Note that not all of these classes have the com.intellij prefix, and also that you can define custom scopes (https://www.jetbrains.com/help/idea/configuring-scopes-and-file-colors.html) to limit your searches, for example, to only idea-xxx.jar files.
If you open an EP's interface or abstract class, it is always helpful to inspect the contents of its package. For instance, the interface of the com.intellij.sdkType EP lives in the com.intellij.openapi.projectRoots package. Inspecting the contents of this package shows many related classes that will be useful if you are implementing this feature.
It is sometimes helpful to search directly for a method, class, and class member if you can guess a part of its name. You can either use Search Everything or Go to Symbol (https://www.jetbrains.com/help/idea/reference-keymap-win-default.html#find_everything). Note that you need to change the search scope to All Places in the search window to find all occurrences of symbols.
If you want to implement a functionality that is similar to an existing IDE feature, but you can't guess the name of the extension point or implementation class, the underlying implementation can be found by the texts displayed in the UI.
Use the displayed text or its part as the target for a search (https://www.jetbrains.com/help/idea/finding-and-replacing-text-in-project.html) within the IntelliJ Community project.
If the text is localized, this will identify a bundle file there the text is defined. Copy the key from the bundle file identified by the search.
Use the key text as the target for a search within the IntelliJ Community project. This search locates the implementation or related class, or plugin configuration file (Plugin Configuration File) that uses the text key in an extension (Extensions) declaration.
If the key is found in the extension declaration in plugin.xml file, find the implementing class attribute value (in most cases it is implementationClass) and navigate to a declaration (https://www.jetbrains.com/help/rider/Navigation_and_Search__Go_to_Declaration.html#74fa64b7), or use attribute value as the target of a class search (https://www.jetbrains.com/help/idea/searching-everywhere.html#Searching_Everywhere.xml) in the IntelliJ Community codebase to find the implementation.
If the text is not localized, the search will most probably find the desired implementation or related class. In this case, search for the found method/class usages, and repeat this until the actual implementation class is found.
As a general remark, the use of implementation classes is strongly discouraged (i.e., classes ending with Impl in their name, located under impl package, or included in *-impl.jar).
API annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) must not be used, see Internal API Migration for more details and replacements.
As a plugin developer, you should enable the internal mode (Enabling Internal Mode) in IntelliJ IDEA. This provides access to a suite of tools to help you develop, debug, and test IntelliJ Platform plugins.
One of its most helpful features is the UI Inspector (Internal Actions - UI Inspector), which lets you investigate all parts of the UI of every IntelliJ-based IDE by simply clicking on them. Equally important is the Tools | Internal Actions | UI Debugger tool. It will display all actions that are run by the IDE when you interact with UI elements, for example, by clicking a button.
Finally, the internal mode provides the Tools | View PSI Structure… and Tools | View PSI Structure of Current File… actions, which allow you to analyze the PSI tree (Program Structure Interface (PSI)), please see documentation (https://www.jetbrains.com/help/idea/psi-viewer.html). The PsiViewer plugin (https://plugins.jetbrains.com/plugin/227-psiviewer) is a separate plugin with similar capabilities for inspecting PSI trees, and it comes with a dedicated tool window that displays information on the fly. However, it does not display information about stubs (Stub Indexes) or formatting models (Code Formatter).
The IntelliJ Platform Explorer (https://plugins.jetbrains.com/intellij-platform-explorer) is a search tool for browsing Extension Points (Extensions) (EP) and Listeners (Listeners) inside existing implementations of all open-source IntelliJ Platform plugins published on JetBrains Marketplace (https://plugins.jetbrains.com). You can navigate directly to the source files to find inspiration when implementing your own extensions and listeners for IntelliJ-based IDEs.
The IntelliJ Platform SDK Documentation should always be the first resource you check for information. Here is a condensed list you can use for further reference:
Section on exploring module and plugin APIs ("Exploring Module and Plugin APIs" in "Plugin Compatibility with IntelliJ Platform Products").
List of notable (Notable Changes and Features in IntelliJ Platform and Plugins API) and incompatible (Incompatible Changes in IntelliJ Platform and Plugins API) API changes.
Use this reference of build number ranges to specify the correct since-build and until-build values in your plugin descriptor.
Setting the actual values in plugin.xml (Plugin Configuration File) is managed by the patchPluginXml ("patchPluginXml" in "Gradle IntelliJ Plugin") Gradle task, see "Patching the Plugin Configuration File" in "Configuring Gradle IntelliJ Plugin".
Please note the following regarding values:
Values must represent the actual build numbers. Any made-up numbers must not be used and such plugins will be rejected on JetBrains Marketplace. For example, 233.* is invalid for since-build; any of 999.*, 234.* (maximum is 233.*) and 223.9999 are invalid for until-build.
Not specifying until-build means it will include all future builds. This includes future, yet unreleased versions and possibly new IDEs, which might impact compatibility later.
To support all releases for a specific branch, use dot-star suffix (.* ) in until-build. For example, 232.* for all 2023.2.x releases.
Compatibility EnforcementBefore publishing, the plugin must be checked using "Plugin Verifier" in "Verifying Plugin Compatibility" against the specified version range (and specified compatible products) to ensure binary compatibility. Any additional reported errors/warnings should also be fixed when possible.
Plugins hosted on JetBrains Marketplace (https://plugins.jetbrains.com) are checked automatically. According to Approval Guidelines (https://plugins.jetbrains.com/legal/approval-guidelines), incompatible plugin versions will be restricted by JetBrains if necessary.
To denote a release, a multipart build number is used. It consists of the following parts:
Product ID (IC for IDEA Community, IU for IDEA Ultimate, RM for RubyMine, PY for PyCharm, PS for PhpStorm, etc.)
Branch number (223)
Build number in the branch (9559)
Branch numbers are based on the YYYY.R IDE release version numbers (https://blog.jetbrains.com/blog/2016/03/09/jetbrains-toolbox-release-and-versioning-changes/). The branch number takes the last two digits of the year and the R release number. For example, 231 for 2023.1, 232 for 2023.2, and 233 for 2023.3.
The build number may have multiple components: IU-162.94.11, IU-162.94.11.256.42. This gives more flexibility for third-party plugins and IDE developers. Plugins may specify compatibility versions more precisely (for example, requiring a specific bugfix release); IDE vendors may have build numbers based on a specific IntelliJ Platform version and specify additional internal version (for example 256.42 in XX-162.94.11.256.42) to allow plugin developers for their IDE to specify compatibility.
Multipart build numbers can also be used in the since-build and until-build attributes of idea-version. Usually you should omit the product ID and use only the branch number and build number, for example:
Build numbers for productsSpecific build numbers and their corresponding release version are available via Previous Releases on the corresponding product's download page, for example Previous IntelliJ IDEA Releases (https://www.jetbrains.com/idea/download/previous.html). For upcoming versions, see Early Access Program (https://eap.jetbrains.com).
See also What versions of IntelliJ-based IDEs are supported? (https://intellij-support.jetbrains.com/hc/en-us/articles/360019574859-What-versions-of-IntelliJ-based-IDEs-are-supported-) for JetBrains IDE support policy.
Which versions should your plugin support? We've collected some insights based on download statistics in Statistics: Product Versions in Use (https://plugins.jetbrains.com/docs/marketplace/product-versions-in-use-statistics.html).
When supporting multiple major versions, it is strongly recommended to build against the lowest supported version to guarantee backwards-compatibility. In the case of supporting a range of platform versions with different underlying Java level (see below), it is required.
Verifying Plugin Compatibility discusses tooling support to ensure compatibility with the chosen version range and IDEs.
Consult Incompatible API Changes (Incompatible Changes in IntelliJ Platform and Plugins API) and Notable API Changes (Notable Changes and Features in IntelliJ Platform and Plugins API) for an overview of known breaking and relevant changes across IDE versions.
In some cases, keeping a dedicated branch and corresponding plugin release for each major IDE version might be required due to incompatibilities that cannot be solved in other ways.
Note that there is no YY0. In the YYYY.R versioning scheme, the R part starts at 1.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Branch number | IntelliJ Platform version |
|---|---|
242 | 2024.2 NOTE Java 21 is now required |
241 (https://github.com/JetBrains/intellij-community/tree/241) | 2024.1 |
233 (https://github.com/JetBrains/intellij-community/tree/233) | 2023.3 |
232 (https://github.com/JetBrains/intellij-community/tree/232) | 2023.2 |
231 (https://github.com/JetBrains/intellij-community/tree/231) | 2023.1 |
223 (https://github.com/JetBrains/intellij-community/tree/223) | 2022.3 |
222 (https://github.com/JetBrains/intellij-community/tree/222) | 2022.2 NOTE Java 17 is now required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) |
Branch number | IntelliJ Platform version |
|---|---|
221 (https://github.com/JetBrains/intellij-community/tree/221) | 2022.1 |
213 (https://github.com/JetBrains/intellij-community/tree/213) | 2021.3 |
212 (https://github.com/JetBrains/intellij-community/tree/212) | 2021.2 |
211 (https://github.com/JetBrains/intellij-community/tree/211) | 2021.1 |
203 (https://github.com/JetBrains/intellij-community/tree/203) | 2020.3 NOTE Java 11 is now required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) |
202 (https://github.com/JetBrains/intellij-community/tree/202) | 2020.2 |
201 (https://github.com/JetBrains/intellij-community/tree/201) | 2020.1 |
193 (https://github.com/JetBrains/intellij-community/tree/193) | 2019.3 |
192 (https://github.com/JetBrains/intellij-community/tree/192) | 2019.2 |
191 (https://github.com/JetBrains/intellij-community/tree/191) | 2019.1 |
When using additional repositories, make sure to use HTTPS always.
JetBrains maintains public repositories that host artifacts related to the IntelliJ Platform, such as binaries and source code. These repositories make artifacts more accessible for plugin developers.
The IntelliJ Platform artifacts repositories are:
Releases repository (https://www.jetbrains.com/intellij-repository/releases/) for release versions by build number (Build Number Ranges).
Snapshots repository (https://www.jetbrains.com/intellij-repository/snapshots/) for BRANCH#-EAP-SNAPSHOT, EAP-CANDIDATE-SNAPSHOT, LATEST-EAP-SNAPSHOT, and the EAP-SNAPSHOT.
See the Maven coordinates section for details about specifying these artifacts.
Both the Releases and Snapshots repositories have two types of content:
Binary and source code artifacts for cross-platform, ZIP distributions of IntelliJ Platform-based IDEs, such as IntelliJ IDEA, CLion, Rider, and MPS. These artifacts are not intended to be accessed directly from a plugin project's Gradle build script. The Gradle IntelliJ Plugin will access them as-needed for a plugin project.
Artifacts for individual modules from the IntelliJ Platform. These may be downloaded, or accessed directly from a Gradle build script, as explained below.
Artifacts for IntelliJ Platform third-party dependencies are hosted at a separate intellij-dependencies (https://cache-redirector.jetbrains.com/intellij-dependencies) repository. A link to this repository should be added to Maven POM or Gradle build script when individual modules from an IntelliJ Platform artifacts repository are used.
Usages of deprecated URL https://jetbrains.bintray.com/intellij-third-party-dependencies must be replaced with https://cache-redirector.jetbrains.com/intellij-dependencies in build scripts.
IntelliJ Platform module artifacts are utilized by adding information to a project's Gradle build script. More information about Gradle support (https://www.jetbrains.com/help/idea/gradle.html) is available in the IntelliJ IDEA Help documentation.
To set up dependencies on a module, there are two types of information needed:
Specify the corresponding repository URL for the artifact.
Specify the Maven coordinates (https://maven.apache.org/pom.html#Maven_Coordinates) for the artifact.
The URL for the desired artifact needs to be added to a Maven or Gradle script:
For release versions, use:
https://www.jetbrains.com/intellij-repository/releases
For EAP snapshots, use:
https://www.jetbrains.com/intellij-repository/snapshots
For dependencies on individual modules from the IntelliJ Platform, also use:
https://cache-redirector.jetbrains.com/intellij-dependencies
Describing a desired IntelliJ Platform module artifact is done with Maven coordinates: groupId, artifactId, and version. The Maven coordinates are based on the names of modules.
The groupId for a module is the prefix com.jetbrains. concatenated with the first two parts of the module name. For example, the module intellij.xml would have the groupId com.jetbrains.intellij.xml.
The artifactId is the second.. n parts of the module name separated by "-" characters. For example, the module intellij.xml would have the artifactId xml. There are some special cases to artifactId names. If the second part of the module name is a common group like platform, vcs, or cloud, the second part of the module name is dropped, and the artifactId becomes the third.. n parts of the module name, separated by "-" characters. Portions of the module name expressed in camelCase format are divided and used in the artifactId as (all lower case) camel-case.
The table below shows some example module names and their corresponding groupId and artifactId.
Module Name | groupId | artifactId |
|---|---|---|
intellij.java.compiler.antTasks | com.jetbrains.intellij.java | java-compiler-ant-tasks |
intellij.java.debugger | com.jetbrains.intellij.java | java-debugger |
intellij.platform.util | com.jetbrains.intellij.platform | util |
intellij.platform.vcs.log | com.jetbrains.intellij.platform | vcs-log |
intellij.spring | com.jetbrains.intellij.spring | spring |
intellij.xml.impl | com.jetbrains.intellij.xml | xml-impl |
The artifact version can be specified in one of several ways because each artifact at the Repository URLs has multiple versions available:
Specify release build versions as MAJOR\[.MINOR]\[.FIX]. For example 14, or 14.1, or 14.1.1
Snapshot versions are specified as:
The snapshot of the most recent branch build is specified as BRANCH-EAP-SNAPSHOT. For example, 193-EAP-SNAPSHOT. There is only one of this type of build for each branch of each product.
The snapshot of the branch from which the next EAP/release build might be produced is specified as BRANCH.BUILD-EAP-CANDIDATE-SNAPSHOT. For example 193.4386-EAP-CANDIDATE-SNAPSHOT. There are multiple builds of this type, one for each build in each branch of every product.
The latest snapshot of a product is always specified as LATEST-EAP-SNAPSHOT. There is only one build of this type per product, and it is always the same as the BRANCH-EAP-SNAPSHOT for the newest branch of the product.
A snapshot of a branch is specified as BRANCH.BUILD.FIX-EAP-SNAPSHOT. For example, 193.4386.10-EAP-SNAPSHOT. There are many builds of this type for each branch of each product.
For example, to specify the jps-model-serialization module:
groupId = com.jetbrains.intellij.platform
artifactId = jps-model-serialization
classifier = ""
packaging = jar
This section presents an example of adding IntelliJ Platform repository and module in a Gradle build script. The example illustrates declaring the artifact URL, Maven coordinates, and version for the jps-model-serialization module artifact. There are two parts to the example: the repository and the dependency sections.
This code snippet selects the release repository with the first URL, and the repository of IntelliJ Platform dependencies with the second URL. The second URL is needed because this example selects individual modules.
repositories {
maven("https://www.jetbrains.com/intellij-repository/releases")
maven("https://cache-redirector.jetbrains.com/intellij-dependencies")
}repositories {
maven { url "https://www.jetbrains.com/intellij-repository/releases" }
maven { url "https://cache-redirector.jetbrains.com/intellij-dependencies" }
}This code snippet specifies the desired module artifacts.
dependencies {
implementation("com.jetbrains.intellij.platform:jps-model-serialization:182.2949.4")
implementation("com.jetbrains.intellij.platform:jps-model-impl:182.2949.4")
}dependencies {
implementation "com.jetbrains.intellij.platform:jps-model-serialization:182.2949.4"
implementation "com.jetbrains.intellij.platform:jps-model-impl:182.2949.4"
}Note:
The artifact version (182.2949.4) must match in both statements.
In this example jps-model-serialization declares the APIs and jps-model-impl provides the implementation, so both are required dependencies.
1069 Extension Points and 195 Listeners for IntelliJ Platform 2024.1
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Analysis.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/resources/META-INF/Analysis.xml)
AnalysisImpl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/resources/META-INF/AnalysisImpl.xml)
builtInServer.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/built-in-server/resources/META-INF/builtInServer.xml)
CodeStyle.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/code-style-impl/resources/META-INF/CodeStyle.xml)
com.intellij.platform.images (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/images/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.images.themeFilter (https://jb.gg/ipe?extensions=com.intellij.images.themeFilter) |
CompletionExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/CompletionExtensionPoints.xml)
CoreImpl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/resources/META-INF/CoreImpl.xml)
DomPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/dom-impl/src/META-INF/DomPlugin.xml)
duplicates-analysis.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/duplicates-analysis/resources/META-INF/duplicates-analysis.xml)
Extension Point | Implementation |
|---|---|
com.intellij.equivalenceDescriptorProvider (https://jb.gg/ipe?extensions=com.intellij.equivalenceDescriptorProvider) | EquivalenceDescriptorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/duplicates-analysis/src/com/intellij/dupLocator/equivalence/EquivalenceDescriptorProvider.java) |
Extension Point | Implementation |
|---|---|
com.intellij.editorFactoryListener (https://jb.gg/ipe?extensions=com.intellij.editorFactoryListener) | EditorFactoryListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/editor/event/EditorFactoryListener.java) |
com.intellij.syntaxHighlighter (https://jb.gg/ipe?extensions=com.intellij.syntaxHighlighter) | SyntaxHighlighter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/fileTypes/SyntaxHighlighter.java) |
EditorExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/EditorExtensionPoints.xml)
ExternalSystemDependencyUpdater.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/dependency-updater/resources/META-INF/ExternalSystemDependencyUpdater.xml)
Extension Point | Implementation |
|---|---|
com.intellij.externalSystem.dependencyModifier (https://jb.gg/ipe?extensions=com.intellij.externalSystem.dependencyModifier) | ExternalDependencyModificator (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-api/dependency-updater/src/com/intellij/externalSystem/ExternalDependencyModificator.java) |
ExternalSystemExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/external-system-impl/resources/META-INF/ExternalSystemExtensionPoints.xml)
FormatterExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/FormatterExtensionPoints.xml)
IdeCore.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core-impl/resources/META-INF/IdeCore.xml)
Extension Point | Implementation |
|---|---|
com.intellij.applicationInitializedListener (https://jb.gg/ipe?extensions=com.intellij.applicationInitializedListener) | ApplicationInitializedListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ide-core/src/com/intellij/ide/ApplicationInitializedListener.kt) |
com.intellij.notificationGroup (https://jb.gg/ipe?extensions=com.intellij.notificationGroup) | n/a |
com.intellij.registryKey (https://jb.gg/ipe?extensions=com.intellij.registryKey) | n/a |
Indexing.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/resources/META-INF/Indexing.xml)
Inspect.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/inspect/resources/META-INF/Inspect.xml)
Extension Point | Implementation |
|---|---|
com.intellij.inspectResultsConsumer (https://jb.gg/ipe?extensions=com.intellij.inspectResultsConsumer) | InspectResultsConsumer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/inspect/src/com/intellij/codeInspection/InspectResultsConsumer.java) |
com.intellij.inspectionApplicationFactory (https://jb.gg/ipe?extensions=com.intellij.inspectionApplicationFactory) | InspectionApplicationFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/inspect/src/com/intellij/codeInspection/InspectionApplicationFactory.kt) |
com.intellij.inspectionGroupProvider (https://jb.gg/ipe?extensions=com.intellij.inspectionGroupProvider) | InspectionGroupProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/inspect/src/com/intellij/codeInspection/inspectionProfile/InspectionGroupProvider.kt) |
intellij.json.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/json/resources/intellij.json.xml)
intellij.notebooks.visualization.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/notebooks/visualization/resources/intellij.notebooks.visualization.xml)
intellij.platform.experiment.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/experiment/resources/intellij.platform.experiment.xml)
Extension Point | Implementation |
|---|---|
com.intellij.experiment.abExperimentOption (https://jb.gg/ipe?extensions=com.intellij.experiment.abExperimentOption) | ABExperimentOption (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/experiment/src/com/intellij/platform/experiment/ab/impl/experiment/ABExperimentOption.kt) |
intellij.platform.feedback.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/feedback/resources/intellij.platform.feedback.xml)
Extension Point | Implementation |
|---|---|
com.intellij.feedback.idleFeedbackSurvey (https://jb.gg/ipe?extensions=com.intellij.feedback.idleFeedbackSurvey) | FeedbackSurvey (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/feedback/src/com/intellij/platform/feedback/FeedbackSurvey.kt) |
intellij.platform.ide.newUiOnboarding.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/new-ui-onboarding/resources/intellij.platform.ide.newUiOnboarding.xml)
Extension Point | Implementation |
|---|---|
com.intellij.ide.newUiOnboarding (https://jb.gg/ipe?extensions=com.intellij.ide.newUiOnboarding) | n/a |
com.intellij.ide.newUiOnboarding.step (https://jb.gg/ipe?extensions=com.intellij.ide.newUiOnboarding.step) | NewUiOnboardingStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/new-ui-onboarding/src/com/intellij/platform/ide/newUiOnboarding/NewUiOnboardingStep.kt) |
intellij.platform.remoteServers.impl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/remote-servers/impl/resources/intellij.platform.remoteServers.impl.xml)
intellij.platform.settings.local.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/settings-local/resources/intellij.platform.settings.local.xml)
Extension Point | Implementation |
|---|---|
com.intellij.settingsController (https://jb.gg/ipe?extensions=com.intellij.settingsController) | DelegatedSettingsController (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/settings/src/com/intellij/platform/settings/SettingsController.kt) |
intellij.platform.statistics.devkit.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/statistics/devkit/resources/intellij.platform.statistics.devkit.xml)
Extension Point | Implementation |
|---|---|
com.intellij.internal.statistic.devkit.toolwindow.logGroupActionsProvider (https://jb.gg/ipe?extensions=com.intellij.internal.statistic.devkit.toolwindow.logGroupActionsProvider) | StatisticsLogGroupActionsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/statistics/devkit/src/com/intellij/internal/statistic/devkit/toolwindow/StatisticsLogGroupActionsProvider.kt) |
intellij.platform.tips.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/tips-of-the-day/resources/intellij.platform.tips.xml)
Extension Point | Implementation |
|---|---|
com.intellij.tipAndTrickPromotionFactory (https://jb.gg/ipe?extensions=com.intellij.tipAndTrickPromotionFactory) | TipAndTrickPromotionFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/tips-of-the-day/src/com/intellij/ide/util/TipAndTrickPromotionFactory.kt) |
LangExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/LangExtensionPoints.xml)
LangExtensions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/LangExtensions.xml)
Extension Point | Implementation |
|---|---|
com.intellij.dependenciesToolWindow.tabProvider (https://jb.gg/ipe?extensions=com.intellij.dependenciesToolWindow.tabProvider) | DependenciesToolWindowTabProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/dependencies-toolwindow/src/com/intellij/dependencytoolwindow/DependenciesToolWindowTabProvider.kt) |
Extension Point | Implementation |
|---|---|
com.intellij.history.activityPresentationProvider (https://jb.gg/ipe?extensions=com.intellij.history.activityPresentationProvider) | ActivityPresentationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lvcs-api/src/com/intellij/history/ActivityPresentationProvider.kt) |
Extension Point | Implementation |
|---|---|
com.intellij.mlCompletionCorrectnessSupporter (https://jb.gg/ipe?extensions=com.intellij.mlCompletionCorrectnessSupporter) | MLCompletionCorrectnessSupporter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ml-impl/src/com/intellij/platform/ml/impl/correctness/MLCompletionCorrectnessSupporter.kt) |
com.intellij.platform.ml.impl.approach (https://jb.gg/ipe?extensions=com.intellij.platform.ml.impl.approach) | MLTaskApproachInitializer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ml-impl/src/com/intellij/platform/ml/impl/MLTask.kt) |
com.intellij.platform.ml.impl.turboComplete.smartPipelineRunner (https://jb.gg/ipe?extensions=com.intellij.platform.ml.impl.turboComplete.smartPipelineRunner) | SmartPipelineRunner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ml-impl/src/com/intellij/platform/ml/impl/turboComplete/SmartPipelineRunner.kt) |
OpenTelemetryExtensions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/diagnostic/telemetry-impl/resources/META-INF/OpenTelemetryExtensions.xml)
Extension Point | Implementation |
|---|---|
com.intellij.openTelemetryExporterProvider (https://jb.gg/ipe?extensions=com.intellij.openTelemetryExporterProvider) | OpenTelemetryExporterProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/diagnostic/telemetry-impl/src/OpenTelemetryExporterProvider.kt) |
PlatformExecutionActions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/META-INF/PlatformExecutionActions.xml)
Extension Point | Implementation |
|---|---|
com.intellij.execution.displayDescriptorChooser (https://jb.gg/ipe?extensions=com.intellij.execution.displayDescriptorChooser) | DisplayDescriptorChooser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/StoppableRunDescriptors.kt) |
com.intellij.multilaunch.condition.template (https://jb.gg/ipe?extensions=com.intellij.multilaunch.condition.template) | ConditionTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/multilaunch/execution/conditions/ConditionTemplate.kt) |
com.intellij.multilaunch.task.definition (https://jb.gg/ipe?extensions=com.intellij.multilaunch.task.definition) | TaskExecutableTemplate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/execution/multilaunch/execution/executables/TaskExecutableTemplate.kt) |
PlatformExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml)
PlatformLangComponents.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/PlatformLangComponents.xml)
Extension Point | Implementation |
|---|---|
com.intellij.registerToolWindowTaskProvider (https://jb.gg/ipe?extensions=com.intellij.registerToolWindowTaskProvider) | RegisterToolWindowTaskProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/toolWindow/toolwindow.kt) |
PlatformWarmup.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/warmup/resources/META-INF/PlatformWarmup.xml)
Extension Point | Implementation |
|---|---|
com.intellij.projectBuildWarmupSupport (https://jb.gg/ipe?extensions=com.intellij.projectBuildWarmupSupport) | ProjectBuildWarmupSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/warmup/src/com/intellij/warmup/ProjectBuildWarmupSupport.kt) |
com.intellij.projectIndexesWarmupSupport (https://jb.gg/ipe?extensions=com.intellij.projectIndexesWarmupSupport) | ProjectIndexesWarmupSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/warmup/src/com/intellij/warmup/ProjectIndexesWarmupSupport.kt) |
ProjectModel.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-api/resources/META-INF/ProjectModel.xml)
ProjectModelImpl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/projectModel-impl/resources/META-INF/ProjectModelImpl.xml)
RefactoringExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/refactoring/resources/META-INF/RefactoringExtensionPoints.xml)
RefactoringLangExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/RefactoringLangExtensionPoints.xml)
RegExpPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/RegExpSupport/resources/META-INF/RegExpPlugin.xml)
smart-update.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/smart-update/resources/META-INF/smart-update.xml)
Extension Point | Implementation |
|---|---|
com.intellij.smartUpdateStep (https://jb.gg/ipe?extensions=com.intellij.smartUpdateStep) | SmartUpdateStep (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/smart-update/src/com/intellij/smartUpdate/SmartUpdateStep.kt) |
smRunner.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/smRunner/resources/META-INF/smRunner.xml)
Extension Point | Implementation |
|---|---|
com.intellij.importTestOutput (https://jb.gg/ipe?extensions=com.intellij.importTestOutput) | ImportTestOutputExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/history/ImportTestOutputExtension.java) |
SpellCheckerPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/spellchecker/src/META-INF/SpellCheckerPlugin.xml)
structuralsearch.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/structuralsearch/resources/META-INF/structuralsearch.xml)
Extension Point | Implementation |
|---|---|
com.intellij.structuralsearch.filterProvider (https://jb.gg/ipe?extensions=com.intellij.structuralsearch.filterProvider) | |
com.intellij.structuralsearch.profile (https://jb.gg/ipe?extensions=com.intellij.structuralsearch.profile) | StructuralSearchProfile (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/structuralsearch/source/com/intellij/structuralsearch/StructuralSearchProfile.java) |
com.intellij.structuralsearch.specialXmlTagExtractor (https://jb.gg/ipe?extensions=com.intellij.structuralsearch.specialXmlTagExtractor) | SpecialElementExtractor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/structuralsearch/source/com/intellij/structuralsearch/SpecialElementExtractor.java) |
Extension Point | Implementation |
|---|---|
com.intellij.tasks.contextProvider (https://jb.gg/ipe?extensions=com.intellij.tasks.contextProvider) | WorkingContextProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/tasks-platform-api/src/com/intellij/tasks/context/WorkingContextProvider.java) |
UsageViewActions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView/resources/idea/UsageViewActions.xml)
Extension Point | Implementation |
|---|---|
com.intellij.generatedSourceUsageFilter (https://jb.gg/ipe?extensions=com.intellij.generatedSourceUsageFilter) | GeneratedSourceUsageFilter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView/src/com/intellij/usages/rules/GeneratedSourceUsageFilter.kt) |
com.intellij.usages.usageReferenceClassProvider (https://jb.gg/ipe?extensions=com.intellij.usages.usageReferenceClassProvider) | UsageReferenceClassProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/usageView/src/com/intellij/usages/impl/UsageReferenceClassProvider.kt) |
vcs-log.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-log/impl/src/META-INF/vcs-log.xml)
VcsExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/vcs-impl/resources/META-INF/VcsExtensionPoints.xml)
WebSymbolsExtensionPoints.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/webSymbols/resources/META-INF/WebSymbolsExtensionPoints.xml)
WorkspaceModelExtensions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/workspace/jps/src/META-INF/WorkspaceModelExtensions.xml)
xdebugger.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/xdebugger-impl/resources/META-INF/xdebugger.xml)
Extension Point | Implementation |
|---|---|
com.intellij.completion.htmlInTextCompletionEnabler (https://jb.gg/ipe?extensions=com.intellij.completion.htmlInTextCompletionEnabler) | HtmlInTextCompletionEnabler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/codeInsight/completion/HtmlInTextCompletionEnabler.java) |
com.intellij.completion.htmlInTextCompletionPopupExtension (https://jb.gg/ipe?extensions=com.intellij.completion.htmlInTextCompletionPopupExtension) | HtmlInTextCompletionPopupExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/impl/src/com/intellij/codeInsight/completion/HtmlInTextCompletionPopupExtension.java) |
com.intellij.html.compatibleLanguage (https://jb.gg/ipe?extensions=com.intellij.html.compatibleLanguage) | n/a |
XmlPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-resources/src/META-INF/XmlPlugin.xml)
602 Extension Points and 86 Listeners for IntelliJ Community Plugins
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
AntSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ant/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
AntSupport.AntMessageCustomizer (https://jb.gg/ipe?extensions=AntSupport.AntMessageCustomizer) | AntMessageCustomizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ant/src/com/intellij/lang/ant/config/execution/AntMessageCustomizer.java) |
ByteCodeViewer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ByteCodeViewer/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
ByteCodeViewer.classSearcher (https://jb.gg/ipe?extensions=ByteCodeViewer.classSearcher) |
com.intellij.completion.evaluation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/evaluation-plugin/resources/META-INF/plugin.xml)
com.intellij.completion.ml.ranking (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/completion-ml-ranking/resources/META-INF/plugin.xml)
com.intellij.copyright (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/copyright/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.copyright.updater (https://jb.gg/ipe?extensions=com.intellij.copyright.updater) | UpdateCopyrightsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdateCopyrightsProvider.java) |
com.intellij.copyright.variablesProvider (https://jb.gg/ipe?extensions=com.intellij.copyright.variablesProvider) | CopyrightVariablesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/copyright/src/com/maddyhome/idea/copyright/pattern/CopyrightVariablesProvider.java) |
com.intellij.gradle (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/plugin-resources/META-INF/plugin.xml)
com.intellij.java-i18n (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/java-i18n/src/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.java-i18n.i18nizeHandlerProvider (https://jb.gg/ipe?extensions=com.intellij.java-i18n.i18nizeHandlerProvider) | I18nizeHandlerProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/java-i18n/src/com/intellij/codeInspection/i18n/I18nizeHandlerProvider.java) |
com.intellij.java-i18n.resourceBundleManager (https://jb.gg/ipe?extensions=com.intellij.java-i18n.resourceBundleManager) | ResourceBundleManager (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/java-i18n/src/com/intellij/lang/properties/psi/ResourceBundleManager.java) |
com.intellij.properties (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/properties/src/META-INF/plugin.xml)
com.intellij.searcheverywhere.ml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/resources/META-INF/plugin.xml)
com.intellij.settingsSync (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/settings-sync/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.settingsSync.settingsProvider (https://jb.gg/ipe?extensions=com.intellij.settingsSync.settingsProvider) | SettingsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/settings-sync/src/com/intellij/settingsSync/SettingsProvider.kt) |
com.intellij.settingsSyncMigration (https://jb.gg/ipe?extensions=com.intellij.settingsSyncMigration) | SettingsSyncMigration (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/settings-sync/src/com/intellij/settingsSync/SettingsSyncMigration.kt) |
com.intellij.stats.completion (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/stats-collector/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.stats.completion.policy (https://jb.gg/ipe?extensions=com.intellij.stats.completion.policy) | CompletionStatsPolicy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/stats-collector/src/com/intellij/stats/completion/CompletionStatsPolicy.kt) |
com.intellij.tasks (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/tasks/tasks-core/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.tasks.commitPlaceholderProvider (https://jb.gg/ipe?extensions=com.intellij.tasks.commitPlaceholderProvider) | CommitPlaceholderProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/tasks-platform-api/src/com/intellij/tasks/CommitPlaceholderProvider.java) |
com.intellij.tasks.dialogPanelProvider (https://jb.gg/ipe?extensions=com.intellij.tasks.dialogPanelProvider) | TaskDialogPanelProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/tasks/tasks-api/src/com/intellij/tasks/ui/TaskDialogPanelProvider.java) |
com.intellij.tasks.repositoryType (https://jb.gg/ipe?extensions=com.intellij.tasks.repositoryType) | TaskRepositoryType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/tasks-platform-api/src/com/intellij/tasks/TaskRepositoryType.java) |
com.intellij.turboComplete (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/turboComplete/resources/META-INF/plugin.xml)
com.intellij.uiDesigner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.paletteItemProvider (https://jb.gg/ipe?extensions=com.intellij.paletteItemProvider) | PaletteItemProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer/src/com/intellij/ide/palette/PaletteItemProvider.java) |
com.intellij.uiDesigner.formInspectionTool (https://jb.gg/ipe?extensions=com.intellij.uiDesigner.formInspectionTool) | FormInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer/src/com/intellij/uiDesigner/inspections/FormInspectionTool.java) |
com.jetbrains.filePrediction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/filePrediction/resources/META-INF/plugin.xml)
com.jetbrains.packagesearch.intellij-plugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/package-search/resources/META-INF/plugin.xml)
com.jetbrains.performancePlugin (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/core/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.jetbrains.performancePlugin.commandProvider (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.commandProvider) | CommandProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/CommandProvider.java) |
com.jetbrains.performancePlugin.profiler (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.profiler) | |
com.jetbrains.performancePlugin.runCallbackHandler (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.runCallbackHandler) | RunCallbackHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/core/src/com/jetbrains/performancePlugin/RunCallbackHandler.java) |
com.jetbrains.performancePlugin.snapshotOpener (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.snapshotOpener) |
completion-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/completion-fe10.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.completionInformationProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.completionInformationProvider) | CompletionInformationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/fe10/analysis/src/org/jetbrains/kotlin/idea/completion/CompletionInformationProvider.kt) |
Extension Point | Implementation |
|---|---|
com.intellij.javaCoverageEngineExtension (https://jb.gg/ipe?extensions=com.intellij.javaCoverageEngineExtension) | JavaCoverageEngineExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage/src/com/intellij/coverage/JavaCoverageEngineExtension.java) |
coverage-common-plugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage-common/src/META-INF/coverage-common-plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.coverageEngine (https://jb.gg/ipe?extensions=com.intellij.coverageEngine) | CoverageEngine (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage-common/src/com/intellij/coverage/CoverageEngine.java) |
com.intellij.coverageOptions (https://jb.gg/ipe?extensions=com.intellij.coverageOptions) | CoverageOptions (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage-common/src/com/intellij/coverage/CoverageOptions.java) |
com.intellij.coverageRunner (https://jb.gg/ipe?extensions=com.intellij.coverageRunner) | CoverageRunner (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/coverage-common/src/com/intellij/coverage/CoverageRunner.java) |
DesignerCorePlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer-core/src/META-INF/DesignerCorePlugin.xml)
Extension Point | Implementation |
|---|---|
Designer.customizations (https://jb.gg/ipe?extensions=Designer.customizations) | DesignerCustomizations (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ui-designer-core/src/com/intellij/designer/DesignerCustomizations.java) |
extensions.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k2/resources/META-INF/extensions.xml)
Extension Point | Implementation |
|---|---|
com.intellij.newProjectWizard.buildSystem.kotlin (https://jb.gg/ipe?extensions=com.intellij.newProjectWizard.buildSystem.kotlin) | n/a |
org.jetbrains.kotlin.codeinsight.quickfix.registrar (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.codeinsight.quickfix.registrar) | KotlinQuickFixRegistrar (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/code-insight/api/src/org/jetbrains/kotlin/idea/codeinsight/api/applicators/fixes/KotlinQuickFixService.kt) |
facets-base.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/facets-base.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.facetConfigurationExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.facetConfigurationExtension) | KotlinFacetConfigurationExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/facet/src/org/jetbrains/kotlin/idea/facet/KotlinFacetConfigurationExtension.kt) |
org.jetbrains.kotlin.versionInfoProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.versionInfoProvider) | KotlinVersionInfoProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/facet/src/org/jetbrains/kotlin/idea/facet/KotlinVersionInfoProvider.kt) |
file-types.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/file-types.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.binaryExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.binaryExtension) | KotlinBinaryExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/base/projectStructure/KotlinBinaryExtension.kt) |
gradle-groovy-integration.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.plugins.gradle.pluginDescriptions (https://jb.gg/ipe?extensions=org.jetbrains.plugins.gradle.pluginDescriptions) | GradlePluginDescriptionsExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/src/codeInsight/GradlePluginDescriptionsExtension.java) |
org.jetbrains.plugins.gradle.resolve.contributor (https://jb.gg/ipe?extensions=org.jetbrains.plugins.gradle.resolve.contributor) | GradleMethodContextContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/src/service/resolve/GradleMethodContextContributor.java) |
groovy-support.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/resources/META-INF/groovy-support.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.idea.maven.importing.groovy.foldersConfiguratorContributor (https://jb.gg/ipe?extensions=org.jetbrains.idea.maven.importing.groovy.foldersConfiguratorContributor) | PluginContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/groovy/GroovyPluginConfigurator.kt) |
highlighting-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-k1/resources/META-INF/highlighting-fe10.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.highlighterExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.highlighterExtension) | KotlinHighlightingVisitorExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-k1/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightingVisitorExtension.kt) |
InspectionGadgets.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/META-INF/InspectionGadgets.xml)
intellij.ae.database.core.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/feature-usage-database/core/resources/intellij.ae.database.core.xml)
Extension Point | Implementation |
|---|---|
com.intellij.ae.database.fusEventCatcher (https://jb.gg/ipe?extensions=com.intellij.ae.database.fusEventCatcher) |
intellij.dev.codeInsight.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/dev/intellij.dev.codeInsight/resources/intellij.dev.codeInsight.xml)
Extension Point | Implementation |
|---|---|
com.intellij.dev.lang.goodCodeRedVisitor (https://jb.gg/ipe?extensions=com.intellij.dev.lang.goodCodeRedVisitor) | GoodCodeRedVisitor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/dev/intellij.dev.codeInsight/src/internal/GoodCodeRedVisitor.java) |
intellij.dev.psiViewer.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/dev/intellij.dev.psiViewer/resources/intellij.dev.psiViewer.xml)
intellij.devkit.core.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml)
intellij.ide.startup.importSettings.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ide-startup/importSettings/resources/intellij.ide.startup.importSettings.xml)
Extension Point | Implementation |
|---|---|
com.intellij.transferSettings.externalProjectImportChecker (https://jb.gg/ipe?extensions=com.intellij.transferSettings.externalProjectImportChecker) | ExternalProjectImportChecker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/ide-startup/importSettings/src/com/intellij/ide/startup/importSettings/transfer/ExternalProjectImportChecker.kt) |
com.intellij.transferSettings.vscode.pluginMapping (https://jb.gg/ipe?extensions=com.intellij.transferSettings.vscode.pluginMapping) |
intellij.java.frontback.impl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-frontback-impl/resource/intellij.java.frontback.impl.xml)
Extension Point | Implementation |
|---|---|
com.intellij.java.definitions (https://jb.gg/ipe?extensions=com.intellij.java.definitions) | AbstractBasicJavaDefinitionService (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-frontback-impl/src/com/intellij/codeInsight/definition/AbstractBasicJavaDefinitionService.java) |
intellij.java.remoteServers.impl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/remote-servers/impl/src/intellij.java.remoteServers.impl.xml)
Extension Point | Implementation |
|---|---|
com.intellij.remoteServer.moduleBuilderContribution (https://jb.gg/ipe?extensions=com.intellij.remoteServer.moduleBuilderContribution) | CloudModuleBuilderContributionFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/remote-servers/impl/src/com/intellij/remoteServer/impl/module/CloudModuleBuilderContributionFactory.java) |
intellij.performanceTesting.remoteDriver.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/remote-driver/resources/intellij.performanceTesting.remoteDriver.xml)
Extension Point | Implementation |
|---|---|
com.jetbrains.performancePlugin.remoteDriver.uiHierarchyExtension (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.remoteDriver.uiHierarchyExtension) | UiHierarchyWebServiceExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/remote-driver/src/com/jetbrains/performancePlugin/remotedriver/webservice/UiHierarchyWebService.kt) |
com.jetbrains.performancePlugin.remoteDriver.xpathDataModelExtension (https://jb.gg/ipe?extensions=com.jetbrains.performancePlugin.remoteDriver.xpathDataModelExtension) | XpathDataModelSubTreeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/performanceTesting/remote-driver/src/com/jetbrains/performancePlugin/remotedriver/xpath/XpathDataModelSubTreeProvider.kt) |
intellij.python.community.impl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/src/intellij.python.community.impl.xml)
intellij.searchEverywhereMl.ranking.core.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/ranking/core/resources/intellij.searchEverywhereMl.ranking.core.xml)
Extension Point | Implementation |
|---|---|
com.intellij.searcheverywhere.ml.rankingModelLoader (https://jb.gg/ipe?extensions=com.intellij.searcheverywhere.ml.rankingModelLoader) | SearchEverywhereMLRankingModelLoader (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/ranking/core/src/com/intellij/searchEverywhereMl/ranking/core/model/SearchEverywhereMLRankingModelLoader.kt) |
com.intellij.searcheverywhere.ml.searchEverywhereElementFeaturesProvider (https://jb.gg/ipe?extensions=com.intellij.searcheverywhere.ml.searchEverywhereElementFeaturesProvider) | SearchEverywhereElementFeaturesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/ranking/core/src/com/intellij/searchEverywhereMl/ranking/core/features/SearchEverywhereElementFeaturesProvider.kt) |
intellij.searchEverywhereMl.ranking.ext.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/ranking/ext/resources/intellij.searchEverywhereMl.ranking.ext.xml)
Extension Point | Implementation |
|---|---|
com.intellij.searchEverywhereMl.searchEverywhereElementKeyProvider (https://jb.gg/ipe?extensions=com.intellij.searchEverywhereMl.searchEverywhereElementKeyProvider) | SearchEverywhereElementKeyProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/ranking/ext/src/com/intellij/searchEverywhereMl/ranking/ext/SearchEverywhereElementKeyProvider.kt) |
intellij.searchEverywhereMl.semantics.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/search-everywhere-ml/semantics/resources/intellij.searchEverywhereMl.semantics.xml)
Extension Point | Implementation |
|---|---|
com.intellij.searcheverywhere.ml.fileIndexableEntitiesProvider (https://jb.gg/ipe?extensions=com.intellij.searcheverywhere.ml.fileIndexableEntitiesProvider) | FileIndexableEntitiesProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/ml-embeddings/src/com/intellij/platform/ml/embeddings/search/indices/FileIndexableEntitiesProvider.kt) |
JavaAnalysisPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-analysis-impl/src/META-INF/JavaAnalysisPlugin.xml)
JavaIndexingPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-indexing-impl/src/META-INF/JavaIndexingPlugin.xml)
JavaPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/META-INF/JavaPlugin.xml)
JavaPsiPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-psi-impl/src/META-INF/JavaPsiPlugin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.idePlatformKind (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idePlatformKind) | IdePlatformKind |
org.jetbrains.kotlin.idePlatformKindResolution (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idePlatformKindResolution) | IdePlatformKindResolution (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/fe10/analysis/src/org/jetbrains/kotlin/caches/resolve/IdePlatformKindResolution.kt) |
org.jetbrains.kotlin.idePlatformKindTooling (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idePlatformKindTooling) | IdePlatformKindTooling (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/code-insight/src/org/jetbrains/kotlin/idea/base/codeInsight/tooling/IdePlatformKindTooling.kt) |
Extension Point | Implementation |
|---|---|
com.intellij.junitListener (https://jb.gg/ipe?extensions=com.intellij.junitListener) | IDEAJUnitListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-runtime/src/com/intellij/rt/execution/junit/IDEAJUnitListener.java) |
com.intellij.testDiscoveryProducer (https://jb.gg/ipe?extensions=com.intellij.testDiscoveryProducer) | TestDiscoveryProducer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/execution/impl/src/com/intellij/execution/testDiscovery/TestDiscoveryProducer.java) |
jvm-debugger.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/jvm-debugger.xml)
Extension Point | Implementation |
|---|---|
com.intellij.debugger.kotlinStackFrameValueContributor (https://jb.gg/ipe?extensions=com.intellij.debugger.kotlinStackFrameValueContributor) | KotlinStackFrameValueContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/stackFrame/KotlinStackFrameValueContributor.kt) |
JvmAnalysisPlugin.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/jvm/jvm-analysis-impl/resources/META-INF/JvmAnalysisPlugin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.resolveScopeEnlarger (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.resolveScopeEnlarger) | KotlinResolveScopeEnlarger (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/base/projectStructure/KotlinResolveScopeEnlarger.kt) |
kotlin-core-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/kotlin-core-fe10.xml)
kotlin-core.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/kotlin-core.xml)
kotlin.base.code-insight.minimal.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/code-insight/minimal/resource/kotlin.base.code-insight.minimal.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.smartEnterProcessorFixer (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.smartEnterProcessorFixer) |
kotlin.gradle.code-insight-common.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/code-insight-common/resources/kotlin.gradle.code-insight-common.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.idea.gradleBuildScriptSupport (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idea.gradleBuildScriptSupport) | GradleBuildScriptSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/code-insight-common/src/org/jetbrains/kotlin/idea/gradleCodeInsightCommon/GradleBuildScriptSupport.kt) |
kotlin.gradle.gradle-java.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/gradle-java/resources/kotlin.gradle.gradle-java.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.gradleProjectImportHandler (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.gradleProjectImportHandler) | GradleProjectImportHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/KotlinGradleSourceSetDataService.kt) |
org.jetbrains.kotlin.mppProjectResolve (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.mppProjectResolve) | KotlinMppGradleProjectResolverExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/configuration/mpp/KotlinMppGradleProjectResolverExtension.kt) |
kotlin.gradle.gradle-tooling.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/gradle-tooling/resources/kotlin.gradle.gradle-tooling.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.idea.gradleTooling.serialization.IdeaKotlinSerializationContext (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idea.gradleTooling.serialization.IdeaKotlinSerializationContext) | IdeaKotlinSerializationContext |
kotlin.gradle.gradle.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/gradle/gradle/resources/kotlin.gradle.gradle.xml)
kotlin.highlighting.k2.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-k2/resources/kotlin.highlighting.k2.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.callHighlighterExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.callHighlighterExtension) | KotlinCallHighlighterExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-k2/src/org/jetbrains/kotlin/idea/highlighting/KotlinCallHighlighterExtension.kt) |
kotlin.highlighting.shared.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-shared/resources/META-INF/kotlin.highlighting.shared.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.beforeResolveHighlightingVisitor (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.beforeResolveHighlightingVisitor) | BeforeResolveHighlightingExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/highlighting/highlighting-minimal/src/org/jetbrains/kotlin/idea/base/highlighting/KotlinBeforeResolveHighlightingPass.kt) |
kotlin.maven.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/maven/resources/kotlin.maven.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.mavenProjectImportHandler (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.mavenProjectImportHandler) | MavenProjectImportHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/maven/src/org/jetbrains/kotlin/idea/maven/KotlinMavenImporter.kt) |
kotlin.project-wizard.idea.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/project-wizard/idea/resources/kotlin.project-wizard.idea.xml)
kotlin.searching.k2.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/kotlin.searching/resources/kotlin.searching.k2.xml)
Extension Point | Implementation |
|---|---|
com.intellij.directKotlinClassInheritorsSearch (https://jb.gg/ipe?extensions=com.intellij.directKotlinClassInheritorsSearch) | QueryExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/QueryExecutor.java) |
kotlinx-serialization.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/compiler-plugins/kotlinx-serialization/common/resources/META-INF/kotlinx-serialization.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.idea.compilerPlugin.kotlinxSerialization.kotlinSerializationEnabledChecker (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idea.compilerPlugin.kotlinxSerialization.kotlinSerializationEnabledChecker) | KotlinSerializationEnabledChecker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/compiler-plugins/kotlinx-serialization/common/src/org/jetbrains/kotlin/idea/compilerPlugin/kotlinxSerialization/KotlinSerializationEnabledChecker.kt) |
libraryJarUsage.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/META-INF/libraryJarUsage.xml)
Extension Point | Implementation |
|---|---|
com.intellij.internal.statistic.libraryUsageImportProcessor (https://jb.gg/ipe?extensions=com.intellij.internal.statistic.libraryUsageImportProcessor) | LibraryUsageImportProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/java-impl/src/com/intellij/internal/statistic/libraryUsage/LibraryUsageImportProcessor.kt) |
light-classes-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/light-classes-fe10.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.ultraLightClassModifierExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.ultraLightClassModifierExtension) | UltraLightClassModifierExtension |
lowLevelApiFir.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k2/resources/META-INF/lowLevelApiFir.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.ktResolveExtensionProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.ktResolveExtensionProvider) | KtResolveExtensionProvider |
org.jetbrains.kotlin.llFirSessionConfigurator (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.llFirSessionConfigurator) | LLFirSessionConfigurator |
ManifestSupport.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/manifest/src/META-INF/ManifestSupport.xml)
Extension Point | Implementation |
|---|---|
com.intellij.manifest.parser.provider (https://jb.gg/ipe?extensions=com.intellij.manifest.parser.provider) | HeaderParserProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/manifest/src/org/jetbrains/lang/manifest/header/HeaderParserProvider.java) |
org.editorconfig.editorconfigjetbrains (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/editorconfig/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
editorconfig.exportProvider (https://jb.gg/ipe?extensions=editorconfig.exportProvider) | EditorConfigExportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/editorconfig/src/org/editorconfig/settings/EditorConfigExportProvider.java) |
editorconfig.optionDescriptorProvider (https://jb.gg/ipe?extensions=editorconfig.optionDescriptorProvider) | EditorConfigOptionDescriptorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/editorconfig/src/org/editorconfig/language/extensions/EditorConfigOptionDescriptorProvider.kt) |
org.intellij.groovy (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/groovy/src/META-INF/plugin.xml)
org.intellij.intelliLang (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
org.intellij.intelliLang.injectionConfig (https://jb.gg/ipe?extensions=org.intellij.intelliLang.injectionConfig) | n/a |
org.intellij.intelliLang.languageSupport (https://jb.gg/ipe?extensions=org.intellij.intelliLang.languageSupport) | LanguageInjectionSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/LanguageInjectionSupport.java) |
org.intellij.plugins.markdown (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/markdown/core/resources/META-INF/plugin.xml)
org.jetbrains.debugger.streams (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/stream-debugger/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.debugger.streams.librarySupport (https://jb.gg/ipe?extensions=org.jetbrains.debugger.streams.librarySupport) | LibrarySupportProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/stream-debugger/src/com/intellij/debugger/streams/lib/LibrarySupportProvider.java) |
org.jetbrains.idea.eclipse (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/eclipse/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.idea.eclipse.natureImporter (https://jb.gg/ipe?extensions=org.jetbrains.idea.eclipse.natureImporter) | EclipseNatureImporter (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/eclipse/src/org/jetbrains/idea/eclipse/importWizard/EclipseNatureImporter.java) |
org.jetbrains.idea.maven (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/maven/src/main/resources/META-INF/plugin.xml)
org.jetbrains.idea.reposearch (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/repository-search/src/main/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.idea.reposearch.provider (https://jb.gg/ipe?extensions=org.jetbrains.idea.reposearch.provider) | DependencySearchProvidersFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/repository-search/src/main/java/org/jetbrains/idea/reposearch/DependencySearchProvidersFactory.java) |
org.jetbrains.plugins.gradle (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/gradle/java/resources/META-INF/plugin.xml)
org.jetbrains.plugins.textmate (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/textmate/src/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.textmate.bundleProvider (https://jb.gg/ipe?extensions=com.intellij.textmate.bundleProvider) | TextMateBundleProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/textmate/src/org/jetbrains/plugins/textmate/api/TextMateBundleProvider.kt) |
org.jetbrains.plugins.yaml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/yaml/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.yaml.customStructureViewFactory (https://jb.gg/ipe?extensions=com.intellij.yaml.customStructureViewFactory) | YAMLCustomStructureViewFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/yaml/src/org/jetbrains/yaml/structureView/YAMLCustomStructureViewFactory.java) |
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.idea.compilerPlugin.parcelize.availabilityProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.idea.compilerPlugin.parcelize.availabilityProvider) | ParcelizeAvailabilityProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/compiler-plugins/parcelize/common/src/org/jetbrains/kotlin/idea/compilerPlugin/parcelize/ParcelizeAvailability.kt) |
PythonParser.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-parser/resources/META-INF/PythonParser.xml)
Extension Point | Implementation |
|---|---|
Pythonid.dialectsTokenSetContributor (https://jb.gg/ipe?extensions=Pythonid.dialectsTokenSetContributor) | PythonDialectsTokenSetContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-parser/src/com/jetbrains/python/PythonDialectsTokenSetContributor.java) |
PythonPsi.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-psi-api/resources/META-INF/PythonPsi.xml)
PythonPsiImpl.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml)
PythonSdk.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-sdk/resources/META-INF/PythonSdk.xml)
Extension Point | Implementation |
|---|---|
Pythonid.projectSdkConfigurationExtension (https://jb.gg/ipe?extensions=Pythonid.projectSdkConfigurationExtension) | PyProjectSdkConfigurationExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-sdk/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfigurationExtension.kt) |
Pythonid.pythonFlavorProvider (https://jb.gg/ipe?extensions=Pythonid.pythonFlavorProvider) | PythonFlavorProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-sdk/src/com/jetbrains/python/sdk/flavors/PythonFlavorProvider.java) |
Pythonid.pythonSdkFlavor (https://jb.gg/ipe?extensions=Pythonid.pythonSdkFlavor) | PythonSdkFlavor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/python/python-sdk/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java) |
refactorings-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/refactorings-fe10.xml)
refactorings.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/refactorings.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.renameHandler (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.renameHandler) |
resolution-fe10.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/resolution-fe10.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.declarationAttributeAltererExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.declarationAttributeAltererExtension) | DeclarationAttributeAltererExtension |
org.jetbrains.kotlin.resolveScopeEnlarger (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.resolveScopeEnlarger) | KotlinResolveScopeEnlarger (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/base/project-structure/src/org/jetbrains/kotlin/idea/base/projectStructure/KotlinResolveScopeEnlarger.kt) |
org.jetbrains.kotlin.syntheticScopeProviderExtension (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.syntheticScopeProviderExtension) | SyntheticScopeProviderExtension |
scripting-base.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/common/resources/META-INF/scripting-base.xml)
scripting-support.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/plugin/k1/resources/META-INF/scripting-support.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.scratchFileLanguageProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.scratchFileLanguageProvider) | ScratchFileLanguageProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/jvm/src/org/jetbrains/kotlin/idea/scratch/ScratchFileLanguageProvider.kt) |
org.jetbrains.kotlin.scriptDefinitionsProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.scriptDefinitionsProvider) | ScriptDefinitionsProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/kotlin/scripting/src/kotlin/script/experimental/intellij/scriptDefinitionProvider.kt) |
Extension Point | Implementation |
|---|---|
com.intellij.runMarkerContributionAdditionalCondition (https://jb.gg/ipe?extensions=com.intellij.runMarkerContributionAdditionalCondition) | ShRunnerAdditionalCondition (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/sh/core/src/com/intellij/sh/run/ShRunnerAdditionalCondition.java) |
com.intellij.shellOccurrencesHighlightingSuppressor (https://jb.gg/ipe?extensions=com.intellij.shellOccurrencesHighlightingSuppressor) | ShOccurrencesHighlightingSuppressor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/sh/core/src/com/intellij/sh/highlighting/ShOccurrencesHighlightingSuppressor.kt) |
tanvd.grazi (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/grazie/resources/META-INF/plugin.xml)
terminal.xml (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/terminal/resources/META-INF/terminal.xml)
Extension Point | Implementation |
|---|---|
com.theoryinpractice.testng.listener (https://jb.gg/ipe?extensions=com.theoryinpractice.testng.listener) | IDEATestNGListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/testng_rt/src/com/intellij/rt/testng/IDEATestNGListener.java) |
Extension Point | Implementation |
|---|---|
XPathView.xpath.contextProviderExtension (https://jb.gg/ipe?extensions=XPathView.xpath.contextProviderExtension) | ContextProviderExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/context/ContextProviderExtension.java) |
XPathView.xpath.functionProvider (https://jb.gg/ipe?extensions=XPathView.xpath.functionProvider) | XPathFunctionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/context/functions/XPathFunctionProvider.java) |
XPathView.xsltRunnerExtension (https://jb.gg/ipe?extensions=XPathView.xsltRunnerExtension) | XsltRunnerExtension (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/run/XsltRunnerExtension.java) |
Product-Specific Plugin Development: Android Studio (Android Studio Plugin Development)
60 Extension Points and 29 Listeners for Android Plugin
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
adt-ui.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/adt-ui/src/main/java/META-INF/adt-ui.xml)
Extension Point | Implementation |
|---|---|
com.intellij.android.toolWindow (https://jb.gg/ipe?extensions=com.intellij.android.toolWindow) | ToolWindowFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowFactory.kt) |
android-adb.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-adb/src/META-INF/android-adb.xml)
Extension Point | Implementation |
|---|---|
com.android.run.deviceNameRenderer (https://jb.gg/ipe?extensions=com.android.run.deviceNameRenderer) | DeviceNameRendererEx (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-adb/src/com/android/tools/idea/ddms/DeviceNameRendererEx.java) |
com.android.tools.idea.deviceProvisioner (https://jb.gg/ipe?extensions=com.android.tools.idea.deviceProvisioner) | DeviceProvisionerFactory (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-adb/src/com/android/tools/idea/deviceprovisioner/DeviceProvisionerFactory.kt) |
android-execution-common.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/execution/common/src/META-INF/android-execution-common.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.execution.common.androidConfigurationExecutorProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.execution.common.androidConfigurationExecutorProvider) |
android-kotlin-extensions-common.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-kotlin/android-extensions-idea-common/src/META-INF/android-kotlin-extensions-common.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.android.model.androidModuleInfoProvider (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.android.model.androidModuleInfoProvider) | AndroidModuleInfoProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-kotlin/android-extensions-idea-common/src/org/jetbrains/kotlin/android/synthetic/idea/AndroidModuleInfoProvider.kt) |
android-kotlin.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-kotlin/idea-android/src/META-INF/android-kotlin.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.kotlin.androidDexer (https://jb.gg/ipe?extensions=org.jetbrains.kotlin.androidDexer) |
android-lang.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-lang/src/META-INF/android-lang.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.lang.androidSql.contextProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.lang.androidSql.contextProvider) |
android-navigator.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-navigator/src/META-INF/android-navigator.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.navigator.androidViewNodeProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.navigator.androidViewNodeProvider) | AndroidViewNodeProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-navigator/src/com/android/tools/idea/navigator/nodes/AndroidViewNodeProvider.kt) |
android-npw.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-npw/src/META-INF/android-npw.xml)
Extension Point | Implementation |
|---|---|
com.android.moduleDescriptionProvider (https://jb.gg/ipe?extensions=com.android.moduleDescriptionProvider) | ModuleDescriptionProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-npw/src/com/android/tools/idea/npw/module/ModuleDescriptionProvider.kt) |
android-plugin.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android/src/META-INF/android-plugin.xml)
android-templates.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-templates/src/META-INF/android-templates.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.templates.additionalTemplateActionsProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.templates.additionalTemplateActionsProvider) | AdditionalTemplateActionsProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/android-templates/src/com/android/tools/idea/templates/AdditionalTemplateActionsProvider.java) |
app-inspector.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/app-inspection/inspector/ide/src/META-INF/app-inspector.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.appinspection.inspector.ide.appInspectorTabProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.appinspection.inspector.ide.appInspectorTabProvider) | AppInspectorTabProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/app-inspection/inspector/ide/src/com/android/tools/idea/appinspection/inspector/ide/AppInspectorTabProvider.kt) |
assistant.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/assistant/src/META-INF/assistant.xml)
compose-designer.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/compose-designer/src/META-INF/compose-designer.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.compose.preview.composeEditorNotificationProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.compose.preview.composeEditorNotificationProvider) | EditorNotificationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java) |
customview.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/designer/customview/src/META-INF/customview.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.customview.preview.customViewEditorNotificationProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.customview.preview.customViewEditorNotificationProvider) | EditorNotificationProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java) |
designer.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/designer/src/META-INF/designer.xml)
device-manager.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/device-manager/src/META-INF/device-manager.xml)
Extension Point | Implementation |
|---|---|
org.jetbrains.android.deviceManagerTab (https://jb.gg/ipe?extensions=org.jetbrains.android.deviceManagerTab) | DeviceManagerTab (https://github.com/JetBrains/android/tree/idea/241.14494.240/device-manager/src/com/android/tools/idea/devicemanager/DeviceManagerTab.java) |
gradle-dsl.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/gradle-dsl/resources/META-INF/gradle-dsl.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.gradleModelProvider (https://jb.gg/ipe?extensions=com.android.tools.gradleModelProvider) | GradleModelProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/gradle-dsl/src/com/android/tools/idea/gradle/dsl/api/GradleModelProvider.java) |
com.android.tools.idea.gradle.dsl.transformerFactory (https://jb.gg/ipe?extensions=com.android.tools.idea.gradle.dsl.transformerFactory) | GradleDslTransformerFactory (https://github.com/JetBrains/android/tree/idea/241.14494.240/gradle-dsl/src/com/android/tools/idea/gradle/dsl/parser/GradleDslTransformerFactory.java) |
org.jetbrains.idea.gradle.dsl.blockModel (https://jb.gg/ipe?extensions=org.jetbrains.idea.gradle.dsl.blockModel) | BlockModelProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/gradle-dsl/src/com/android/tools/idea/gradle/dsl/model/GradleBlockModelMap.java) |
layout-inspector.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/layout-inspector/src/META-INF/layout-inspector.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.layoutinspector.pipeline.appinspection.compose.getComposeLayoutInspectorJarToken (https://jb.gg/ipe?extensions=com.android.tools.idea.layoutinspector.pipeline.appinspection.compose.getComposeLayoutInspectorJarToken) | GetComposeLayoutInspectorJarToken (https://github.com/JetBrains/android/tree/idea/241.14494.240/layout-inspector/src/com/android/tools/idea/layoutinspector/pipeline/appinspection/compose/ComposeLayoutInspectorClient.kt) |
lint-plugin.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/lint/src/META-INF/lint-plugin.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.lint.common.lintIdeSupport (https://jb.gg/ipe?extensions=com.android.tools.idea.lint.common.lintIdeSupport) | LintIdeSupport (https://github.com/JetBrains/android/tree/idea/241.14494.240/lint/src/com/android/tools/idea/lint/common/LintIdeSupport.kt) |
com.android.tools.idea.lint.common.lintQuickFixProvider (https://jb.gg/ipe?extensions=com.android.tools.idea.lint.common.lintQuickFixProvider) | LintIdeQuickFixProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/lint/src/com/android/tools/idea/lint/common/LintIdeQuickFixProvider.java) |
naveditor.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/nav/editor/src/META-INF/naveditor.xml)
Extension Point | Implementation |
|---|---|
com.android.tools.idea.naveditor.editor.addDestinationMenuToken (https://jb.gg/ipe?extensions=com.android.tools.idea.naveditor.editor.addDestinationMenuToken) | AddDestinationMenuToken (https://github.com/JetBrains/android/tree/idea/241.14494.240/nav/editor/src/com/android/tools/idea/naveditor/editor/AddDestinationMenu.kt) |
com.android.tools.idea.naveditor.surface.navDesignSurfaceToken (https://jb.gg/ipe?extensions=com.android.tools.idea.naveditor.surface.navDesignSurfaceToken) | NavDesignSurfaceToken (https://github.com/JetBrains/android/tree/idea/241.14494.240/nav/editor/src/com/android/tools/idea/naveditor/surface/NavDesignSurfaceToken.java) |
project-system-gradle-plugin.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system-gradle/src/META-INF/project-system-gradle-plugin.xml)
Extension Point | Implementation |
|---|---|
com.android.gradle.sync.postSyncProjectCleanupStep (https://jb.gg/ipe?extensions=com.android.gradle.sync.postSyncProjectCleanupStep) | ProjectCleanupStep (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system-gradle/src/com/android/tools/idea/gradle/project/sync/setup/post/ProjectCleanupStep.java) |
com.android.gradle.sync.postSyncProjectSetupStep (https://jb.gg/ipe?extensions=com.android.gradle.sync.postSyncProjectSetupStep) | ProjectSetupStep (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system-gradle/src/com/android/tools/idea/gradle/project/sync/setup/post/ProjectSetupStep.java) |
com.android.moduleImporter (https://jb.gg/ipe?extensions=com.android.moduleImporter) | AndroidModuleImporter (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system-gradle/src/com/android/tools/idea/gradle/project/AndroidModuleImporter.kt) |
project-system-plugin.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system/src/META-INF/project-system-plugin.xml)
Extension Point | Implementation |
|---|---|
com.android.androidStartupActivity (https://jb.gg/ipe?extensions=com.android.androidStartupActivity) | AndroidStartupActivity (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system/src/com/android/tools/idea/AndroidStartupActivity.kt) |
com.android.project.projectsystem (https://jb.gg/ipe?extensions=com.android.project.projectsystem) | AndroidProjectSystemProvider (https://github.com/JetBrains/android/tree/idea/241.14494.240/project-system/src/com/android/tools/idea/projectsystem/AndroidProjectSystemProvider.kt) |
resources-explorer.xml (https://github.com/JetBrains/android/tree/idea/241.14494.240/android/src/com/android/tools/idea/ui/resourcemanager/META-INF/resources-explorer.xml)
Extension Point | Implementation |
|---|---|
com.android.resourceImporter (https://jb.gg/ipe?extensions=com.android.resourceImporter) | ResourceImporter (https://github.com/JetBrains/android/tree/idea/241.14494.240/android/src/com/android/tools/idea/ui/resourcemanager/plugin/ResourceImporter.kt) |
com.android.resourceViewer (https://jb.gg/ipe?extensions=com.android.resourceViewer) | DesignAssetRenderer (https://github.com/JetBrains/android/tree/idea/241.14494.240/android/src/com/android/tools/idea/ui/resourcemanager/plugin/DesignAssetRenderer.kt) |
Product-Specific Plugin Development: AppCode (AppCode Plugin Development)
34 Extension Points and 6 Listeners for AppCode 2022.3
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
With the release of AppCode 2022.3, we're sunsetting the product. Please see this blog post (https://blog.jetbrains.com/appcode/2022/12/appcode-2022-3-release-and-end-of-sales-and-support/) for details.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Topic | Listener |
|---|---|
CocoaPodsUtils#GEM_TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.cidr.cocoapods.CocoaPodsUtils.GemListener) | GemListener |
CocoaPodsUtils#PODS_TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.cidr.cocoapods.CocoaPodsUtils.PodsListener) | PodsListener |
AMDeviceManager#DEVICE_LISTENER_TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.cidr.execution.deviceSupport.AMDeviceListener) | AMDeviceListener |
XcodeProjectTestListener#TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.cidr.xcode.model.XcodeProjectTestListener) | XcodeProjectTestListener |
XcodeIsBrokenListener.Companion#XCODE_IS_BROKEN_TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.cidr.xcode.refresh.XcodeIsBrokenListener) | XcodeIsBrokenListener |
SwiftPackageManagerSettingsListener.Companion#TOPIC (https://jb.gg/ipe/listeners?topics=com.jetbrains.swift.swiftpm.SwiftPackageManagerSettingsListener) | SwiftPackageManagerSettingsListener |
Extension Point | Implementation |
|---|---|
appcode.additionalRootsProvider (https://jb.gg/ipe?extensions=appcode.additionalRootsProvider) | XcodeMetaDataAdditionalRootsProvider |
appcode.attachDebuggerProvider (https://jb.gg/ipe?extensions=appcode.attachDebuggerProvider) | AppCodeAttachDebuggerExtension |
appcode.projectNameUpdateVeto (https://jb.gg/ipe?extensions=appcode.projectNameUpdateVeto) | XcodeProjectNameUpdateVeto |
appcode.projectRootNodeDelegateProvider (https://jb.gg/ipe?extensions=appcode.projectRootNodeDelegateProvider) | AppCodeProjectRootNodeDelegateProvider |
appcode.rootsInfoPostProcessor (https://jb.gg/ipe?extensions=appcode.rootsInfoPostProcessor) | XcodeRootsInfoPostProcessor |
appcode.runConfigurationExtension (https://jb.gg/ipe?extensions=appcode.runConfigurationExtension) | AppCodeRunConfigurationExtension |
appcode.xcodeExternalBuildProvider (https://jb.gg/ipe?extensions=appcode.xcodeExternalBuildProvider) | XcodeExternalBuildProvider |
appcode.xcodeTemplatePathsProvider (https://jb.gg/ipe?extensions=appcode.xcodeTemplatePathsProvider) | XcodeTemplatePathsProvider |
appcode.xcodeTemplatesProvider (https://jb.gg/ipe?extensions=appcode.xcodeTemplatesProvider) | XcodeTemplatesProvider |
Extension Point | Implementation |
|---|---|
appcode.breakpointHandlersProvider (https://jb.gg/ipe?extensions=appcode.breakpointHandlersProvider) | IPhoneBreakpointHandlersProvider |
appcode.lldbInitializerProvider (https://jb.gg/ipe?extensions=appcode.lldbInitializerProvider) | LLDBInitializerProvider |
cidr.cocoa.xcodeProjectFileProvider (https://jb.gg/ipe?extensions=cidr.cocoa.xcodeProjectFileProvider) | XcodeProjectFileProvider |
Extension Point | Implementation |
|---|---|
cidr.cocoa.documentation.search.candidates.helper (https://jb.gg/ipe?extensions=cidr.cocoa.documentation.search.candidates.helper) | XcodeDocumentationCandidateBasedSearchHelper |
cidr.cocoa.documentation.search.usr.provider (https://jb.gg/ipe?extensions=cidr.cocoa.documentation.search.usr.provider) | XcodeDocumentationUsrProvider |
Extension Point | Implementation |
|---|---|
cidr.lang.swiftTypeInheritorsSearch (https://jb.gg/ipe?extensions=cidr.lang.swiftTypeInheritorsSearch) | QueryExecutor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/util/QueryExecutor.java) |
swift.lang.libraryModuleImportRestriction (https://jb.gg/ipe?extensions=swift.lang.libraryModuleImportRestriction) | SwiftLibraryModuleImportRestriction |
swift.sdkInfo.extractor (https://jb.gg/ipe?extensions=swift.sdkInfo.extractor) | OCResolveConfigurationSdkInfoExtractor |
swift.sourcekit.blacklistedModulesProvider (https://jb.gg/ipe?extensions=swift.sourcekit.blacklistedModulesProvider) | SourceKitBlacklistedModulesProvider |
swift.sourcekit.dependenciesLoader (https://jb.gg/ipe?extensions=swift.sourcekit.dependenciesLoader) | SourceKitPlatformBinaryDependenciesLoader |
Extension Point | Implementation |
|---|---|
cidr.lang.swiftCustomIncludePathProvider (https://jb.gg/ipe?extensions=cidr.lang.swiftCustomIncludePathProvider) | SwiftCustomIncludePathProvider |
cidr.lang.swiftSourceModuleProvider (https://jb.gg/ipe?extensions=cidr.lang.swiftSourceModuleProvider) | SwiftSourceModuleProvider |
swift.kotlinNative (https://jb.gg/ipe?extensions=swift.kotlinNative) | KotlinNativeExtensionPoint |
swift.lang.sourceKit.compileArgumentsCollector (https://jb.gg/ipe?extensions=swift.lang.sourceKit.compileArgumentsCollector) | SwiftSourceKitCompileArgumentsCollector |
swift.lang.sourceKit.dataGenerator (https://jb.gg/ipe?extensions=swift.lang.sourceKit.dataGenerator) | SourceKitDataGenerator |
swift.lang.sourceKit.declarationLocationValidator (https://jb.gg/ipe?extensions=swift.lang.sourceKit.declarationLocationValidator) | SourceKitDeclarationLocationValidator |
swift.lang.sourceKitFixExtension (https://jb.gg/ipe?extensions=swift.lang.sourceKitFixExtension) | SwiftSourceKitFixExtension |
swift.lang.swiftSupportProvider (https://jb.gg/ipe?extensions=swift.lang.swiftSupportProvider) | SwiftSupportProvider |
Extension Point | Implementation |
|---|---|
swift.packageManager.appleSdk.filter (https://jb.gg/ipe?extensions=swift.packageManager.appleSdk.filter) | SwiftPackageLoadedAppleSdkFilter |
swift.packageManager.environmentConfigurator (https://jb.gg/ipe?extensions=swift.packageManager.environmentConfigurator) | SwiftPackageManagerEnvironmentConfigurator |
swift.packageManager.launcher (https://jb.gg/ipe?extensions=swift.packageManager.launcher) | SwiftPackageManagerConfigurationLauncher |
swift.packageManager.modulemapsCollector (https://jb.gg/ipe?extensions=swift.packageManager.modulemapsCollector) | SwiftPackageModuleMapsCollector |
swift.packageManager.systemModuleResolver (https://jb.gg/ipe?extensions=swift.packageManager.systemModuleResolver) | SwiftPackageManagerSystemModuleResolver |
Extension Point | Implementation |
|---|---|
test.cidr.OCCodeInsightDelegate (https://jb.gg/ipe?extensions=test.cidr.OCCodeInsightDelegate) | OCCodeInsightDelegate |
Extension Point | Implementation |
|---|---|
xcode.pbxReferenceBuildSettingsProvider (https://jb.gg/ipe?extensions=xcode.pbxReferenceBuildSettingsProvider) | PBXReferenceBuildSettingProvider |
Product-Specific Plugin Development: CLion (CLion Plugin Development)
135 Extension Points and 40 Listeners for CLion
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Extension Point | Implementation |
|---|---|
clangd.clangTidyAnnotationApplier (https://jb.gg/ipe?extensions=clangd.clangTidyAnnotationApplier) | ClangTidyAnnotationApplier |
clangd.clangTidyResolveInfoProvider (https://jb.gg/ipe?extensions=clangd.clangTidyResolveInfoProvider) | ClangTidyResolveInfoProvider |
clangd.clangdAnnotatorUtil (https://jb.gg/ipe?extensions=clangd.clangdAnnotatorUtil) | ClangAnnotatorUtil |
clangd.clangdBridge (https://jb.gg/ipe?extensions=clangd.clangdBridge) | ClangdBridgeInterface |
clangd.externalCompletionProvider (https://jb.gg/ipe?extensions=clangd.externalCompletionProvider) | ExternalCompletionProvider |
Extension Point | Implementation |
|---|---|
cidr.coverage.coverageComposer (https://jb.gg/ipe?extensions=cidr.coverage.coverageComposer) | CidrCoverageComposer |
cidr.coverage.coverageComposerRunner (https://jb.gg/ipe?extensions=cidr.coverage.coverageComposerRunner) | CidrCoverageComposerRunner |
cidr.coverage.coverageDataFileProvider (https://jb.gg/ipe?extensions=cidr.coverage.coverageDataFileProvider) | CidrCoverageDataFileProvider |
cidr.coverage.coverageErrorProcessor (https://jb.gg/ipe?extensions=cidr.coverage.coverageErrorProcessor) | CidrCoverageErrorProcessor |
cidr.coverage.coverageViewExtensionProvider (https://jb.gg/ipe?extensions=cidr.coverage.coverageViewExtensionProvider) | CidrCoverageViewExtensionProvider |
cidr.coverage.gcovCoverageToolsProvider (https://jb.gg/ipe?extensions=cidr.coverage.gcovCoverageToolsProvider) | GCovCoverageToolProvider |
cidr.coverage.llvmCoverageToolsProvider (https://jb.gg/ipe?extensions=cidr.coverage.llvmCoverageToolsProvider) | LLVMCoverageToolsProvider |
Extension Point | Implementation |
|---|---|
cidr.debugger.backendConsoleInjectionHelper (https://jb.gg/ipe?extensions=cidr.debugger.backendConsoleInjectionHelper) | BackendConsoleInjectionHelper |
cidr.debugger.customDebuggerProvider (https://jb.gg/ipe?extensions=cidr.debugger.customDebuggerProvider) | CidrCustomDebuggerProvider |
cidr.debugger.debugProcessConfigurator (https://jb.gg/ipe?extensions=cidr.debugger.debugProcessConfigurator) | CidrDebugProcessConfigurator |
cidr.debugger.debuggerMessagesProvider (https://jb.gg/ipe?extensions=cidr.debugger.debuggerMessagesProvider) | CidrDebuggerMessagesProvider |
cidr.debugger.disasmRegisterProvider (https://jb.gg/ipe?extensions=cidr.debugger.disasmRegisterProvider) | CidrDisasmRegisterProvider |
cidr.debugger.editorsExtension (https://jb.gg/ipe?extensions=cidr.debugger.editorsExtension) | CidrDebuggerEditorsExtension |
cidr.debugger.formatters.natvis.provider (https://jb.gg/ipe?extensions=cidr.debugger.formatters.natvis.provider) | NatvisFileProvider |
cidr.debugger.frameChildrenContributor (https://jb.gg/ipe?extensions=cidr.debugger.frameChildrenContributor) | CidrFrameChildrenContributor |
cidr.debugger.languageSupport (https://jb.gg/ipe?extensions=cidr.debugger.languageSupport) | CidrDebuggerLanguageSupport |
cidr.debugger.lineBreakpointFileTypesProvider (https://jb.gg/ipe?extensions=cidr.debugger.lineBreakpointFileTypesProvider) | CidrLineBreakpointFileTypesProvider |
cidr.debugger.localVariablesFilterHandler (https://jb.gg/ipe?extensions=cidr.debugger.localVariablesFilterHandler) | LocalVariablesFilterHandler |
cidr.debugger.valueRendererExtension (https://jb.gg/ipe?extensions=cidr.debugger.valueRendererExtension) | ValueRendererExtension |
cidr.debugger.valueRendererFactory (https://jb.gg/ipe?extensions=cidr.debugger.valueRendererFactory) | ValueRendererFactory |
Extension Point | Implementation |
|---|---|
cidr.buildConfigurationProvider (https://jb.gg/ipe?extensions=cidr.buildConfigurationProvider) | CidrBuildConfigurationProvider |
cidr.projectTaskContextProvider (https://jb.gg/ipe?extensions=cidr.projectTaskContextProvider) | CidrProjectTaskContextProvider |
cidr.resolveConfigurationProvider (https://jb.gg/ipe?extensions=cidr.resolveConfigurationProvider) | CidrResolveConfigurationProvider |
cidr.runConfigurationExtension (https://jb.gg/ipe?extensions=cidr.runConfigurationExtension) | CidrRunConfigurationExtensionBase |
cidr.targetConfigurationHelper (https://jb.gg/ipe?extensions=cidr.targetConfigurationHelper) | CidrTargetConfigurationHelper |
cidr.testFrameworkDetector (https://jb.gg/ipe?extensions=cidr.testFrameworkDetector) | CidrTestFrameworkDetector |
Extension Point | Implementation |
|---|---|
cidr.lang.addToProjectFixProvider (https://jb.gg/ipe?extensions=cidr.lang.addToProjectFixProvider) | OCAddToProjectFixProvider |
cidr.lang.appleFrameworkFilter (https://jb.gg/ipe?extensions=cidr.lang.appleFrameworkFilter) | AppleFrameworkFilter |
cidr.lang.codeInsightUnavailabilityHighlighter (https://jb.gg/ipe?extensions=cidr.lang.codeInsightUnavailabilityHighlighter) | OCCodeInsightUnavailabilityHighlighter |
cidr.lang.compilerKindProvider (https://jb.gg/ipe?extensions=cidr.lang.compilerKindProvider) | OCCompilerKindProvider |
cidr.lang.compilerResolver (https://jb.gg/ipe?extensions=cidr.lang.compilerResolver) | OCCompilerResolver |
cidr.lang.fileScopeProvider (https://jb.gg/ipe?extensions=cidr.lang.fileScopeProvider) | OCFileScopeProvider |
cidr.lang.fileWideHighlighter (https://jb.gg/ipe?extensions=cidr.lang.fileWideHighlighter) | FileWideHighlighter |
cidr.lang.headerSearchRootFactory (https://jb.gg/ipe?extensions=cidr.lang.headerSearchRootFactory) | HeadersSearchRootFactory |
cidr.lang.resolveConfigurationSelector (https://jb.gg/ipe?extensions=cidr.lang.resolveConfigurationSelector) | OCResolveConfigurationSelector |
cidr.projectModel.deserializingVetoCondition (https://jb.gg/ipe?extensions=cidr.projectModel.deserializingVetoCondition) | OCWorkspaceDeserializingVetoCondition |
cidr.projectModel.msvcPchHelper (https://jb.gg/ipe?extensions=cidr.projectModel.msvcPchHelper) | OCMsvcPchHelper |
cidr.projectModel.runAfterOCWorkspaceIsInitialized (https://jb.gg/ipe?extensions=cidr.projectModel.runAfterOCWorkspaceIsInitialized) | RunAfterOCWorkspaceIsInitialized |
cidr.projectModel.runAfterOCWorkspaceIsLoaded (https://jb.gg/ipe?extensions=cidr.projectModel.runAfterOCWorkspaceIsLoaded) | RunAfterOCWorkspaceIsLoaded |
cidr.projectModel.searchScopeProvider (https://jb.gg/ipe?extensions=cidr.projectModel.searchScopeProvider) | CidrSearchScopeProvider |
cidr.projectModel.supportedFileChecker (https://jb.gg/ipe?extensions=cidr.projectModel.supportedFileChecker) | OCSupportedFileChecker |
cidr.projectModel.unloadedResolveContextsManager (https://jb.gg/ipe?extensions=cidr.projectModel.unloadedResolveContextsManager) | OCUnloadedResolveContextsManager |
cidr.projectModel.workspaceLoadedCheck (https://jb.gg/ipe?extensions=cidr.projectModel.workspaceLoadedCheck) | OCWorkspaceLoadedChecker |
com.intellij.cidrCommandLineParser (https://jb.gg/ipe?extensions=com.intellij.cidrCommandLineParser) | CidrCommandLineParser |
Extension Point | Implementation |
|---|---|
cidr.lang.testing.potentialTestHolderRootsProvider (https://jb.gg/ipe?extensions=cidr.lang.testing.potentialTestHolderRootsProvider) | CidrPotentialTestHolderRootsProvider |
cidr.lang.testing.testIndexContributor (https://jb.gg/ipe?extensions=cidr.lang.testing.testIndexContributor) | CidrTestIndexContributor |
Extension Point | Implementation |
|---|---|
cidr.devEnvironmentChecker (https://jb.gg/ipe?extensions=cidr.devEnvironmentChecker) | DevEnvironmentChecker |
Extension Point | Implementation |
|---|---|
cidr.util.pluginPathMapper (https://jb.gg/ipe?extensions=cidr.util.pluginPathMapper) | CidrPluginPathMapper |
Extension Point | Implementation |
|---|---|
cidr.markRootActionAvailability (https://jb.gg/ipe?extensions=cidr.markRootActionAvailability) | CidrMarkRootActionAvailability |
cidr.project.is.known.checker (https://jb.gg/ipe?extensions=cidr.project.is.known.checker) | KnownProjectChecker |
cidr.project.rootsBuilderProvider (https://jb.gg/ipe?extensions=cidr.project.rootsBuilderProvider) | Provider |
cidr.project.workspaceProvider (https://jb.gg/ipe?extensions=cidr.project.workspaceProvider) | CidrWorkspaceProvider |
com.jetbrains.cidr.fus.projectModelTypeProvider (https://jb.gg/ipe?extensions=com.jetbrains.cidr.fus.projectModelTypeProvider) | CidrProjectModelTypeProvider |
Extension Point | Implementation |
|---|---|
com.intellij.clangFormatProvider (https://jb.gg/ipe?extensions=com.intellij.clangFormatProvider) | ClangFormatChangeSettingsProvider |
Extension Point | Implementation |
|---|---|
com.intellij.cmake.buildStep (https://jb.gg/ipe?extensions=com.intellij.cmake.buildStep) | CMakeBuildProcessListenerCreator |
com.intellij.cmake.languageKindRecognizer (https://jb.gg/ipe?extensions=com.intellij.cmake.languageKindRecognizer) | CMakeLanguageKindRecognizer |
com.intellij.cmake.loadOnStartupDependency (https://jb.gg/ipe?extensions=com.intellij.cmake.loadOnStartupDependency) | FutureProvider |
com.intellij.cmake.notificationActionProvider (https://jb.gg/ipe?extensions=com.intellij.cmake.notificationActionProvider) | AdditionalActionProvider |
com.intellij.cmake.profileLoadContributor (https://jb.gg/ipe?extensions=com.intellij.cmake.profileLoadContributor) | FutureProvider |
com.intellij.cmake.runnerStep (https://jb.gg/ipe?extensions=com.intellij.cmake.runnerStep) | CMakeRunnerStep |
com.intellij.cmake.targetToConfigProvider (https://jb.gg/ipe?extensions=com.intellij.cmake.targetToConfigProvider) | CMakeTargetToConfigProvider |
Extension Point | Implementation |
|---|---|
clion.buildToolWindowActivator.contributor (https://jb.gg/ipe?extensions=clion.buildToolWindowActivator.contributor) | Contributor |
clion.compoundConfigurationContext (https://jb.gg/ipe?extensions=clion.compoundConfigurationContext) | CidrCompoundConfigurationContext |
clion.externalConfigurationProvider (https://jb.gg/ipe?extensions=clion.externalConfigurationProvider) | CLionExternalConfigurationProvider |
clion.showAssembly.funcInfoProvider (https://jb.gg/ipe?extensions=clion.showAssembly.funcInfoProvider) | CLionShowAssemblyFuncInfoProvider |
Extension Point | Implementation |
|---|---|
clion.externalLoadNotificationAware (https://jb.gg/ipe?extensions=clion.externalLoadNotificationAware) | CLionExternalLoadNotificationAware |
Extension Point | Implementation |
|---|---|
clion.makefile.buildSystemDetector (https://jb.gg/ipe?extensions=clion.makefile.buildSystemDetector) | MkBuildSystemDetector |
clion.makefile.projectPreConfigurator (https://jb.gg/ipe?extensions=clion.makefile.projectPreConfigurator) | MkProjectPreConfigurator |
Extension Point | Implementation |
|---|---|
cidr.openWizardStepProvider (https://jb.gg/ipe?extensions=cidr.openWizardStepProvider) | OpenWizardStepProvider |
Extension Point | Implementation |
|---|---|
com.intellij.cmake.bundledDocumentationProvider (https://jb.gg/ipe?extensions=com.intellij.cmake.bundledDocumentationProvider) | CMakeBundledDocumentationProvider |
com.intellij.cmake.completion.environmentProvider (https://jb.gg/ipe?extensions=com.intellij.cmake.completion.environmentProvider) | CMakeEnvironmentVariableProvider |
com.intellij.cmake.fileLocationProvider (https://jb.gg/ipe?extensions=com.intellij.cmake.fileLocationProvider) | CMakeFileLocationProvider |
Extension Point | Implementation |
|---|---|
cidr.uml.dragAndDropReceiver (https://jb.gg/ipe?extensions=cidr.uml.dragAndDropReceiver) | UmlDiagramDragAndDropReceiver |
cidr.uml.umlDiagramProvider (https://jb.gg/ipe?extensions=cidr.uml.umlDiagramProvider) | UmlDiagramLanguage |
Extension Point | Implementation |
|---|---|
cidr.profiler.memory.environmentProvider (https://jb.gg/ipe?extensions=cidr.profiler.memory.environmentProvider) | MemoryProfileEnvironmentProvider |
cidr.profiler.memory.presentation (https://jb.gg/ipe?extensions=cidr.profiler.memory.presentation) | MemoryProfilePresentation |
cidr.profiler.valgrind.disabler (https://jb.gg/ipe?extensions=cidr.profiler.valgrind.disabler) | ValgrindDisabler |
cidr.profiler.valgrind.executionContext (https://jb.gg/ipe?extensions=cidr.profiler.valgrind.executionContext) | ValgrindExecutionContext |
Extension Point | Implementation |
|---|---|
training.clion.lessons (https://jb.gg/ipe?extensions=training.clion.lessons) | CLionExternalLessons |
Extension Point | Implementation |
|---|---|
cidr.cpp.runFile.entryPointDetector (https://jb.gg/ipe?extensions=cidr.cpp.runFile.entryPointDetector) | CppFileEntryPointDetector |
Extension Point | Implementation |
|---|---|
com.intellij.rml.dfa.devtools.debug.provider (https://jb.gg/ipe?extensions=com.intellij.rml.dfa.devtools.debug.provider) | DfaDebugProvider |
Product-Specific Plugin Development: DataGrip (DataGrip Plugin Development)
75 Extension Points and 28 Listeners for DataGrip
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Extension Point | Implementation |
|---|---|
com.intellij.database.datagrid.extractorsHelper (https://jb.gg/ipe?extensions=com.intellij.database.datagrid.extractorsHelper) | ExtractorsHelper |
com.intellij.database.datagrid.formatterCreatorProvider (https://jb.gg/ipe?extensions=com.intellij.database.datagrid.formatterCreatorProvider) | FormatterCreatorProvider |
com.intellij.database.datagrid.objectNormalizerProvider (https://jb.gg/ipe?extensions=com.intellij.database.datagrid.objectNormalizerProvider) | ObjectNormalizerProvider |
Extension Point | Implementation |
|---|---|
com.intellij.database.datagrid.valueEditorTab (https://jb.gg/ipe?extensions=com.intellij.database.datagrid.valueEditorTab) | ValueEditorTab |
Extension Point | Implementation |
|---|---|
com.intellij.database.mongo.resolveHelper (https://jb.gg/ipe?extensions=com.intellij.database.mongo.resolveHelper) | MongoJSResolveHelper |
Product-Specific Plugin Development: GoLand (GoLand Plugin Development)
17 Extension Points and 5 Listeners for GoLand
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Topic | Listener |
|---|---|
GoLibrariesService#LIBRARIES_TOPIC (https://jb.gg/ipe/listeners?topics=com.goide.project.GoLibrariesService.LibrariesListener) | LibrariesListener |
GoModuleSettings#BUILD_TARGET_TOPIC (https://jb.gg/ipe/listeners?topics=com.goide.project.GoModuleSettings.BuildTargetListener) | BuildTargetListener |
GoModuleSettings#GO_SUPPORT_TOPIC (https://jb.gg/ipe/listeners?topics=com.goide.project.GoModuleSettings.GoSupportListener) | GoSupportListener |
GoProjectLifecycleListener#TOPIC (https://jb.gg/ipe/listeners?topics=com.goide.project.GoProjectLifecycleListener) | GoProjectLifecycleListener |
VgoProjectSettings#VGO_INTEGRATION_TOPIC (https://jb.gg/ipe/listeners?topics=com.goide.vgo.configuration.VgoProjectSettings.IntegrationListener) | IntegrationListener |
Extension Point | Implementation |
|---|---|
com.goide.documentation.additionalDocumentationProvider (https://jb.gg/ipe?extensions=com.goide.documentation.additionalDocumentationProvider) | GoAdditionalDocumentationProvider |
com.goide.importPathsProvider (https://jb.gg/ipe?extensions=com.goide.importPathsProvider) | GoImportPathsProvider |
com.goide.imports.weigher (https://jb.gg/ipe?extensions=com.goide.imports.weigher) | GoImportsWeigher |
com.goide.rootsProvider (https://jb.gg/ipe?extensions=com.goide.rootsProvider) | GoRootsProvider |
Repository: intellij-plugins (https://github.com/JetBrains/intellij-plugins)
Overview of Extension Points and Listeners for open source plugins available in IntelliJ IDEA Ultimate and other IDEs.
25 Extension Points and 9 Listeners
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
AngularJS (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/Angular/resources/META-INF/plugin.xml)
com.intellij.dts (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/dts/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.clion.dtsUtil (https://jb.gg/ipe?extensions=com.intellij.clion.dtsUtil) | DtsCLionUtil (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/dts/src/com/intellij/dts/clion/DtsCLionUtil.kt) |
com.intellij.flex (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/flex/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.flex.breakpoint.type.provider (https://jb.gg/ipe?extensions=com.intellij.flex.breakpoint.type.provider) | BreakpointTypeProvider (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/flex/src/com/intellij/lang/javascript/flex/debug/FlexBreakpointsHandler.java) |
com.thoughtworks.gauge (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/gauge/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.thoughtworks.gauge.moduleImporter (https://jb.gg/ipe?extensions=com.thoughtworks.gauge.moduleImporter) | GaugeModuleImporter (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/gauge/src/com/thoughtworks/gauge/wizard/GaugeModuleImporter.java) |
Extension Point | Implementation |
|---|---|
Dart.completionExtension (https://jb.gg/ipe?extensions=Dart.completionExtension) | DartCompletionExtension (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/Dart/src/com/jetbrains/lang/dart/ide/completion/DartCompletionExtension.java) |
Dart.completionTimerExtension (https://jb.gg/ipe?extensions=Dart.completionTimerExtension) | DartCompletionTimerExtension (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/Dart/src/com/jetbrains/lang/dart/ide/completion/DartCompletionTimerExtension.java) |
Extension Point | Implementation |
|---|---|
org.jetbrains.plugins.cucumber.injector.injectorExtensionPoint (https://jb.gg/ipe?extensions=org.jetbrains.plugins.cucumber.injector.injectorExtensionPoint) | GherkinInjectorExtensionPoint (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/cucumber/src/org/jetbrains/plugins/cucumber/injector/GherkinInjectorExtensionPoint.java) |
org.jetbrains.plugins.cucumber.steps.cucumberJvmExtensionPoint (https://jb.gg/ipe?extensions=org.jetbrains.plugins.cucumber.steps.cucumberJvmExtensionPoint) | CucumberJvmExtensionPoint (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/cucumber/src/org/jetbrains/plugins/cucumber/CucumberJvmExtensionPoint.java) |
idea.plugin.protoeditor (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/protobuf/resources/META-INF/plugin.xml)
intellij.prettierJS (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/prettierJS/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.prettierjs.codeStyleInstaller (https://jb.gg/ipe?extensions=com.intellij.prettierjs.codeStyleInstaller) | PrettierCodeStyleInstaller (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/prettierJS/src/com/intellij/prettierjs/codeStyle/PrettierCodeStyleInstaller.java) |
name.kropp.intellij.makefile (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/makefile/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.makefile.toolWindowStripeController (https://jb.gg/ipe?extensions=com.intellij.makefile.toolWindowStripeController) | MakefileToolWindowStripeController (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/makefile/src/com/jetbrains/lang/makefile/toolWindow/MakefileToolWindowStripeController.kt) |
org.jetbrains.plugins.vue (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/vuejs/resources/META-INF/plugin.xml)
Extension Point | Implementation |
|---|---|
com.intellij.vuejs.containerInfoProvider (https://jb.gg/ipe?extensions=com.intellij.vuejs.containerInfoProvider) | VueContainerInfoProvider (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/vuejs/src/org/jetbrains/vuejs/model/source/VueContainerInfoProvider.kt) |
com.intellij.vuejs.templateScopesProvider (https://jb.gg/ipe?extensions=com.intellij.vuejs.templateScopesProvider) | VueTemplateScopesProvider (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/vuejs/src/org/jetbrains/vuejs/codeInsight/template/VueTemplateScopesProvider.kt) |
Extension Point | Implementation |
|---|---|
Osmorc.frameworkIntegrator (https://jb.gg/ipe?extensions=Osmorc.frameworkIntegrator) | FrameworkIntegrator (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/osmorc/src/org/osmorc/frameworkintegration/FrameworkIntegrator.java) |
Extension Point | Implementation |
|---|---|
com.intellij.tslint.configDetector (https://jb.gg/ipe?extensions=com.intellij.tslint.configDetector) | TsLintConfigDetector (https://github.com/JetBrains/intellij-plugins/tree/idea/241.14494.240/tslint/src/com/intellij/lang/javascript/linter/tslint/config/TsLintConfigDetector.java) |
Product-Specific Plugin Development: PhpStorm (PhpStorm Plugin Development)
61 Extension Points and 11 Listeners for PHP
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Extension Point | Implementation |
|---|---|
com.intellij.phing.phpFileDescriptionProvider (https://jb.gg/ipe?extensions=com.intellij.phing.phpFileDescriptionProvider) | PhingPhpFileDescriptionProvider |
com.intellij.phing.propertyFilesManager (https://jb.gg/ipe?extensions=com.intellij.phing.propertyFilesManager) | PropertyFilesManager |
Extension Point | Implementation |
|---|---|
com.jetbrains.php.tools.quality.Psalm.PsalmConfigurationProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.tools.quality.Psalm.PsalmConfigurationProvider) | PsalmConfigurationProvider |
Extension Point | Implementation |
|---|---|
com.jetbrains.php.tools.quality.PhpStan.PhpStanConfigurationProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.tools.quality.PhpStan.PhpStanConfigurationProvider) | PhpStanConfigurationProvider |
Extension Point | Implementation |
|---|---|
com.jetbrains.php.behat.gherkinContextProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.behat.gherkinContextProvider) | ContextInterfaceProvider |
Extension Point | Implementation |
|---|---|
com.jetbrains.php.framework.descriptionProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.framework.descriptionProvider) | FrameworkDescriptionProvider |
Extension Point | Implementation |
|---|---|
com.jetbrains.php.remote.interpreter.ui.customConfigProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.remote.interpreter.ui.customConfigProvider) | PhpProjectConfigComponentProvider |
com.jetbrains.php.remote.phpHelperScriptProvider (https://jb.gg/ipe?extensions=com.jetbrains.php.remote.phpHelperScriptProvider) | PhpHelperScriptProvider |
com.jetbrains.php.remote.remoteProcessManager (https://jb.gg/ipe?extensions=com.jetbrains.php.remote.remoteProcessManager) | PhpRemoteProcessManager |
Product-Specific Plugin Development: Rider (Rider Plugin Development)
128 Extension Points and 7 Listeners for Rider
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Extension Point | Implementation |
|---|---|
com.jetbrains.dotTrace.dotMemory.runtime.detector (https://jb.gg/ipe?extensions=com.jetbrains.dotTrace.dotMemory.runtime.detector) | DotTraceRuntimeDetector |
Extension Point | Implementation |
|---|---|
com.jetbrains.rider-cpp.run.configurations.cpp (https://jb.gg/ipe?extensions=com.jetbrains.rider-cpp.run.configurations.cpp) | CppConfigurationParametersExtension |
com.jetbrains.rider-cpp.run.configurations.cpp.launch.profile (https://jb.gg/ipe?extensions=com.jetbrains.rider-cpp.run.configurations.cpp.launch.profile) | CppProjectLaunchProfile |
Extension Point | Implementation |
|---|---|
com.intellij.rider.blazorDialectSubstitutor (https://jb.gg/ipe?extensions=com.intellij.rider.blazorDialectSubstitutor) | BlazorHtmlDialectSubstitutor |
Extension Point | Implementation |
|---|---|
com.intellij.backend.actions.support (https://jb.gg/ipe?extensions=com.intellij.backend.actions.support) | RiderActionSupportPolicy |
com.intellij.backend.auto.import.support (https://jb.gg/ipe?extensions=com.intellij.backend.auto.import.support) | RiderAutoImportSupportPolicy |
com.intellij.backend.autoPopup.support (https://jb.gg/ipe?extensions=com.intellij.backend.autoPopup.support) | RiderAutoPopupSupportPolicy |
com.intellij.backend.markup.adapterFactory (https://jb.gg/ipe?extensions=com.intellij.backend.markup.adapterFactory) | FrontendMarkupAdapterFactory |
com.intellij.backend.typedHandler (https://jb.gg/ipe?extensions=com.intellij.backend.typedHandler) | FrontendTypedHandler |
com.intellij.code.cleanup.support (https://jb.gg/ipe?extensions=com.intellij.code.cleanup.support) | RiderCodeCleanupSupportPolicy |
com.intellij.completion.completionSessionStrategy (https://jb.gg/ipe?extensions=com.intellij.completion.completionSessionStrategy) | CompletionSessionStrategy |
com.intellij.frontend.completion.helper (https://jb.gg/ipe?extensions=com.intellij.frontend.completion.helper) | ICompletionHelper |
com.intellij.lang.altEnter (https://jb.gg/ipe?extensions=com.intellij.lang.altEnter) | BulbMenuModelFactory |
com.intellij.lang.altEnter.popupModelDelegate (https://jb.gg/ipe?extensions=com.intellij.lang.altEnter.popupModelDelegate) | PopupModelDelegate |
com.intellij.projectModelViewUpdater (https://jb.gg/ipe?extensions=com.intellij.projectModelViewUpdater) | ProjectModelViewUpdater |
com.intellij.protocolComponentFactory (https://jb.gg/ipe?extensions=com.intellij.protocolComponentFactory) | ProtocolComponentFactory |
com.intellij.rdclient.preemptiveCompletionSuppressor (https://jb.gg/ipe?extensions=com.intellij.rdclient.preemptiveCompletionSuppressor) | PreemptiveCompletionSuppressor |
com.intellij.rdclient.typingPolicy (https://jb.gg/ipe?extensions=com.intellij.rdclient.typingPolicy) | CustomTypingSessionPolicy |
com.intellij.rider.action.fallback.strategy (https://jb.gg/ipe?extensions=com.intellij.rider.action.fallback.strategy) | RiderAsyncBackendDelegatingActionFallbackStrategy |
com.intellij.rider.altEnter.layouter (https://jb.gg/ipe?extensions=com.intellij.rider.altEnter.layouter) | RiderAltEnterLayouter |
com.intellij.rider.backendCrashAnalyser (https://jb.gg/ipe?extensions=com.intellij.rider.backendCrashAnalyser) | BackendCrashAnalyzer |
com.intellij.rider.backendLogXmlPathProvider (https://jb.gg/ipe?extensions=com.intellij.rider.backendLogXmlPathProvider) | RiderCustomBackendLogXmlPathProvider |
com.intellij.rider.breakingWorkflowChangeInterceptor (https://jb.gg/ipe?extensions=com.intellij.rider.breakingWorkflowChangeInterceptor) | RiderBreakingWorkflowChangeInterceptor |
com.intellij.rider.codeStyleContentConverter (https://jb.gg/ipe?extensions=com.intellij.rider.codeStyleContentConverter) | RiderCodeStyleContentPageConverter |
com.intellij.rider.credentials.provider (https://jb.gg/ipe?extensions=com.intellij.rider.credentials.provider) | ICredentialsProvider |
com.intellij.rider.diagnostics.specialPathsProvider (https://jb.gg/ipe?extensions=com.intellij.rider.diagnostics.specialPathsProvider) | SpecialPathsProvider |
com.intellij.rider.documentBehaviour (https://jb.gg/ipe?extensions=com.intellij.rider.documentBehaviour) | RiderDocumentBehaviour |
com.intellij.rider.extraSettingsSync (https://jb.gg/ipe?extensions=com.intellij.rider.extraSettingsSync) | ExtraSettingsSync |
com.intellij.rider.fileBreadcrumbExtensions (https://jb.gg/ipe?extensions=com.intellij.rider.fileBreadcrumbExtensions) | CustomFileBreadcrumbExtensions |
com.intellij.rider.grave.filter (https://jb.gg/ipe?extensions=com.intellij.rider.grave.filter) | RiderHighlightingGraveFilter |
com.intellij.rider.namingPageProvider (https://jb.gg/ipe?extensions=com.intellij.rider.namingPageProvider) | NamingPageProvider |
com.intellij.rider.protocol.hostEnvProvider (https://jb.gg/ipe?extensions=com.intellij.rider.protocol.hostEnvProvider) | RiderBackendEnvProvider |
com.intellij.rider.riderApplicationPreloadListener (https://jb.gg/ipe?extensions=com.intellij.rider.riderApplicationPreloadListener) | RiderApplicationPreloadListener |
com.intellij.rider.wrappedMergeableIconProvider (https://jb.gg/ipe?extensions=com.intellij.rider.wrappedMergeableIconProvider) | RiderWrappedMergeableIconProvider |
com.intellij.selfProfilingPaths.customizer (https://jb.gg/ipe?extensions=com.intellij.selfProfilingPaths.customizer) | DotnetSelfProfilerPathsCustomizer |
com.intellij.solutionLoadNotification (https://jb.gg/ipe?extensions=com.intellij.solutionLoadNotification) | SolutionLoadNotification |
Extension Point | Implementation |
|---|---|
com.intellij.rider.projectModelIconProvider (https://jb.gg/ipe?extensions=com.intellij.rider.projectModelIconProvider) | ProjectModelIconProvider |
com.intellij.rider.workspaceCountableProjectsPolicy (https://jb.gg/ipe?extensions=com.intellij.rider.workspaceCountableProjectsPolicy) | CountableProjectsPolicy |
Extension Point | Implementation |
|---|---|
rider.cpp.debugProcessExtension (https://jb.gg/ipe?extensions=rider.cpp.debugProcessExtension) | RiderCppDebugProcessExtension |
Extension Point | Implementation |
|---|---|
com.intellij.rider.cpp.debuggerSettings (https://jb.gg/ipe?extensions=com.intellij.rider.cpp.debuggerSettings) | CppDebuggerSettings |
Extension Point | Implementation |
|---|---|
com.intellij.rider.database.connectionStringRetriever (https://jb.gg/ipe?extensions=com.intellij.rider.database.connectionStringRetriever) | DatabaseConnectionUrlRetriever |
com.intellij.rider.database.connectionStringToJdbcUrlMapper (https://jb.gg/ipe?extensions=com.intellij.rider.database.connectionStringToJdbcUrlMapper) | ConnectionStringToJdbcUrlConverter |
com.intellij.rider.database.connectionStringsFactory (https://jb.gg/ipe?extensions=com.intellij.rider.database.connectionStringsFactory) | ConnectionStringsFactory |
com.intellij.rider.database.connectionStringsFinder (https://jb.gg/ipe?extensions=com.intellij.rider.database.connectionStringsFinder) | ConnectionStringsFinder |
com.intellij.rider.database.dotnetDataProvider (https://jb.gg/ipe?extensions=com.intellij.rider.database.dotnetDataProvider) | DotnetDataProvider |
com.intellij.rider.database.jdbcUrlToConnectionStringConverter (https://jb.gg/ipe?extensions=com.intellij.rider.database.jdbcUrlToConnectionStringConverter) | JdbcUrlToConnectionStringConverter |
com.intellij.rider.database.schemaCompareDataModelCreatedListener (https://jb.gg/ipe?extensions=com.intellij.rider.database.schemaCompareDataModelCreatedListener) | SchemaCompareDataModelCreatedListener |
Extension Point | Implementation |
|---|---|
com.intellij.rider.dockerDebugProvider (https://jb.gg/ipe?extensions=com.intellij.rider.dockerDebugProvider) | IRiderDockerDebugProvider |
com.intellij.rider.dockerDeploymentTransformer (https://jb.gg/ipe?extensions=com.intellij.rider.dockerDeploymentTransformer) | RiderDockerDeploymentTransformer |
Extension Point | Implementation |
|---|---|
JavaScript.packageJson.configuration.handler (https://jb.gg/ipe?extensions=JavaScript.packageJson.configuration.handler) | RiderPackageJsonConfiguratorHandler |
Extension Point | Implementation |
|---|---|
com.intellij.rider.client.typedHandler (https://jb.gg/ipe?extensions=com.intellij.rider.client.typedHandler) | RiderClientLookupTypedHandler |
Extension Point | Implementation |
|---|---|
com.intellij.rider.refactoringPageProvider (https://jb.gg/ipe?extensions=com.intellij.rider.refactoringPageProvider) | RefactoringPageProvider |
Extension Point | Implementation |
|---|---|
com.intellij.fileSystemExplorerCustomization (https://jb.gg/ipe?extensions=com.intellij.fileSystemExplorerCustomization) | FileSystemExplorerCustomization |
com.intellij.moveProviderExtension (https://jb.gg/ipe?extensions=com.intellij.moveProviderExtension) | MoveProviderExtension |
com.intellij.nestingRulesLanguageExtensions (https://jb.gg/ipe?extensions=com.intellij.nestingRulesLanguageExtensions) | RiderNestingRulesLanguageExtensions |
com.intellij.nugetCredentialProvider (https://jb.gg/ipe?extensions=com.intellij.nugetCredentialProvider) | NuGetCredentialProvider |
com.intellij.openDirectoryExtensions (https://jb.gg/ipe?extensions=com.intellij.openDirectoryExtensions) | OpenDirectoryExtensions |
com.intellij.projectModelViewExtensions (https://jb.gg/ipe?extensions=com.intellij.projectModelViewExtensions) | ProjectModelViewExtensions |
com.intellij.projectTemplateCustomizer (https://jb.gg/ipe?extensions=com.intellij.projectTemplateCustomizer) | ProjectTemplateCustomizer |
com.intellij.projectTemplateProvider (https://jb.gg/ipe?extensions=com.intellij.projectTemplateProvider) | RiderProjectTemplateProvider |
com.intellij.projectTemplateProviderNew (https://jb.gg/ipe?extensions=com.intellij.projectTemplateProviderNew) | ProjectTemplateProvider |
com.intellij.rider.ProfileActionPrinter (https://jb.gg/ipe?extensions=com.intellij.rider.ProfileActionPrinter) | ProfileActionPrinter |
com.intellij.rider.ProjectTypesProvider (https://jb.gg/ipe?extensions=com.intellij.rider.ProjectTypesProvider) | RiderProjectTypesProvider |
com.intellij.rider.SolutionFileTypesProvider (https://jb.gg/ipe?extensions=com.intellij.rider.SolutionFileTypesProvider) | SolutionFileTypesProvider |
com.intellij.rider.action.technical.support.info.provider (https://jb.gg/ipe?extensions=com.intellij.rider.action.technical.support.info.provider) | RiderTechnicalSupportInfoProvider |
com.intellij.rider.breakpoint.customPanelProvider (https://jb.gg/ipe?extensions=com.intellij.rider.breakpoint.customPanelProvider) | IDotNetLineBreakpointCustomPanelsProvider |
com.intellij.rider.breakpoint.customPopupActionsProvider (https://jb.gg/ipe?extensions=com.intellij.rider.breakpoint.customPopupActionsProvider) | IDotNetLineBreakpointPopupActionsProvider |
com.intellij.rider.build.riderBuildConsoleDecorator (https://jb.gg/ipe?extensions=com.intellij.rider.build.riderBuildConsoleDecorator) | RiderBuildConsoleDecorator |
com.intellij.rider.buildButtonModeProvider (https://jb.gg/ipe?extensions=com.intellij.rider.buildButtonModeProvider) | BuildButtonModeProvider |
com.intellij.rider.cleanupAction (https://jb.gg/ipe?extensions=com.intellij.rider.cleanupAction) | CleanupAction |
com.intellij.rider.codeLens.vcsDeclarationRangesProvider (https://jb.gg/ipe?extensions=com.intellij.rider.codeLens.vcsDeclarationRangesProvider) | VcsDeclarationRangesProvider |
com.intellij.rider.completion.csharpIdentifierPartHelper (https://jb.gg/ipe?extensions=com.intellij.rider.completion.csharpIdentifierPartHelper) | CSharpIdentifierPartHelper |
com.intellij.rider.completion.preselectionStrategy (https://jb.gg/ipe?extensions=com.intellij.rider.completion.preselectionStrategy) | RiderFrontendLanguagesPreselectionStrategy |
com.intellij.rider.configurationExecutorExtension (https://jb.gg/ipe?extensions=com.intellij.rider.configurationExecutorExtension) | RiderConfigurationExecutorExtension |
com.intellij.rider.configurationLaunchSettingsExtension (https://jb.gg/ipe?extensions=com.intellij.rider.configurationLaunchSettingsExtension) | RiderConfigurationLaunchSettingsExtension |
com.intellij.rider.consoleFilter (https://jb.gg/ipe?extensions=com.intellij.rider.consoleFilter) | RiderConsoleFilterExtension |
com.intellij.rider.debug.breakpoint.handler.factory (https://jb.gg/ipe?extensions=com.intellij.rider.debug.breakpoint.handler.factory) | IDotNetSupportedBreakpointHandlerFactory |
com.intellij.rider.debugger.value.evaluator.factory (https://jb.gg/ipe?extensions=com.intellij.rider.debugger.value.evaluator.factory) | RiderCustomComponentEvaluatorFactory |
com.intellij.rider.debugger.value.presenter (https://jb.gg/ipe?extensions=com.intellij.rider.debugger.value.presenter) | RiderDebuggerValuePresenter |
com.intellij.rider.debuggerSupportPolicy (https://jb.gg/ipe?extensions=com.intellij.rider.debuggerSupportPolicy) | RiderDebuggerSupportPolicy |
com.intellij.rider.editSourceSuppressor (https://jb.gg/ipe?extensions=com.intellij.rider.editSourceSuppressor) | RiderEditSourceSuppressor |
com.intellij.rider.extendedCodeStructure (https://jb.gg/ipe?extensions=com.intellij.rider.extendedCodeStructure) | RiderExtendedFileStructure |
com.intellij.rider.externalDirectoryProvider (https://jb.gg/ipe?extensions=com.intellij.rider.externalDirectoryProvider) | ExternalDirectoryProvider |
com.intellij.rider.fileTemplating.postCreateAction (https://jb.gg/ipe?extensions=com.intellij.rider.fileTemplating.postCreateAction) | RiderNewFileFromTemplateExtension |
com.intellij.rider.findPopupProjectScopeProvider (https://jb.gg/ipe?extensions=com.intellij.rider.findPopupProjectScopeProvider) | FindPopupProjectScopeProvider |
com.intellij.rider.guidPresenter (https://jb.gg/ipe?extensions=com.intellij.rider.guidPresenter) | GuidGeneratorPresenter |
com.intellij.rider.ideaInspectionBackendSuppressionSupport (https://jb.gg/ipe?extensions=com.intellij.rider.ideaInspectionBackendSuppressionSupport) | IdeaInspectionBackendSuppressionSupport |
com.intellij.rider.newFileListener (https://jb.gg/ipe?extensions=com.intellij.rider.newFileListener) | RiderNewFileListener |
com.intellij.rider.newRunConfigurationTreeGroupingProvider (https://jb.gg/ipe?extensions=com.intellij.rider.newRunConfigurationTreeGroupingProvider) | RiderNewRunConfigurationTreeGroupingProvider |
com.intellij.rider.patchCommandLine (https://jb.gg/ipe?extensions=com.intellij.rider.patchCommandLine) | PatchCommandLineExtension |
com.intellij.rider.pencils.filters.provider (https://jb.gg/ipe?extensions=com.intellij.rider.pencils.filters.provider) | PencilsFiltersProvider |
com.intellij.rider.pencils.inspectionToolGroup (https://jb.gg/ipe?extensions=com.intellij.rider.pencils.inspectionToolGroup) | n/a |
com.intellij.rider.pencils.pencilsFilterGroup (https://jb.gg/ipe?extensions=com.intellij.rider.pencils.pencilsFilterGroup) | n/a |
com.intellij.rider.problemsView.actionsHandler (https://jb.gg/ipe?extensions=com.intellij.rider.problemsView.actionsHandler) | RiderProblemsViewActionsHandler |
com.intellij.rider.problemsView.problems.notifier (https://jb.gg/ipe?extensions=com.intellij.rider.problemsView.problems.notifier) | ProblemsViewNotifier |
com.intellij.rider.problemsView.problems.processor (https://jb.gg/ipe?extensions=com.intellij.rider.problemsView.problems.processor) | RiderProblemsDiffProcessor |
com.intellij.rider.projectView.actions.projectTemplating.backend.reSharperProjectTemplateCustomizer (https://jb.gg/ipe?extensions=com.intellij.rider.projectView.actions.projectTemplating.backend.reSharperProjectTemplateCustomizer) | ReSharperProjectTemplateCustomizer |
com.intellij.rider.publish.publishSettingsProvider (https://jb.gg/ipe?extensions=com.intellij.rider.publish.publishSettingsProvider) | IPublishRuntimeCoreSettingsProvider |
com.intellij.rider.publishConfigurationProvider (https://jb.gg/ipe?extensions=com.intellij.rider.publishConfigurationProvider) | RiderContextPublishProvider |
com.intellij.rider.refactoringPageProvider (https://jb.gg/ipe?extensions=com.intellij.rider.refactoringPageProvider) | RefactoringPageProvider |
com.intellij.rider.resolveContextWidgetProvider (https://jb.gg/ipe?extensions=com.intellij.rider.resolveContextWidgetProvider) | RiderResolveContextWidgetProvider |
com.intellij.rider.run.configurations.dotNetExe (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.dotNetExe) | DotNetExeConfigurationExtension |
com.intellij.rider.run.configurations.externalRunConfigurationGenerator (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.externalRunConfigurationGenerator) | ExternalRunConfigurationGeneratorExtension |
com.intellij.rider.run.configurations.host (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.host) | RunConfigurationHostExtensions |
com.intellij.rider.run.configurations.host.executor (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.host.executor) | RunConfigurationHostExecutorExtensions |
com.intellij.rider.run.configurations.launchSettings (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.launchSettings) | LaunchSettingsConfigurationExtension |
com.intellij.rider.run.configurations.launchSettings.command (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.launchSettings.command) | LaunchSettingsCommandExtension |
com.intellij.rider.run.configurations.multiPlatform.mac.extension (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.multiPlatform.mac.extension) | MacRunConfigurationExtension |
com.intellij.rider.run.configurations.project (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.project) | DotNetProjectConfigurationExtension |
com.intellij.rider.run.configurations.riderCoreDumpConfigurationProvider (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.riderCoreDumpConfigurationProvider) | RiderCoreDumpConfigurationProvider |
com.intellij.rider.run.configurations.uwp (https://jb.gg/ipe?extensions=com.intellij.rider.run.configurations.uwp) | UwpConfigurationExtension |
com.intellij.rider.runToPopupShowPolicy (https://jb.gg/ipe?extensions=com.intellij.rider.runToPopupShowPolicy) | RiderRunToPopupShowPolicy |
com.intellij.rider.runWidgetSuspenderExtension (https://jb.gg/ipe?extensions=com.intellij.rider.runWidgetSuspenderExtension) | RunWidgetSuspenderExtension |
com.intellij.rider.runtime.dotNetRuntimeAutodetect (https://jb.gg/ipe?extensions=com.intellij.rider.runtime.dotNetRuntimeAutodetect) | DotNetRuntimeAutodetect |
com.intellij.rider.solutionConfigurationPresenter (https://jb.gg/ipe?extensions=com.intellij.rider.solutionConfigurationPresenter) | SolutionConfigurationPresenter |
com.intellij.rider.solutionConfigurationToolbarCustomizer (https://jb.gg/ipe?extensions=com.intellij.rider.solutionConfigurationToolbarCustomizer) | SolutionConfigurationToolbarCustomizer |
com.intellij.rider.unitTesting.actionsProvider (https://jb.gg/ipe?extensions=com.intellij.rider.unitTesting.actionsProvider) | RiderUnitTestActionsProvider |
com.intellij.rider.unitTesting.sessionHandler (https://jb.gg/ipe?extensions=com.intellij.rider.unitTesting.sessionHandler) | IRiderUnitTestDebuggerSessionsHandler |
com.intellij.rider.unityDetector (https://jb.gg/ipe?extensions=com.intellij.rider.unityDetector) | UnityDetector |
com.intellij.rider.web.extensions.companionDebugStarter (https://jb.gg/ipe?extensions=com.intellij.rider.web.extensions.companionDebugStarter) | DotNetCompanionDebugStarter |
com.intellij.rider.web.extensions.webBrowserDebugSupport (https://jb.gg/ipe?extensions=com.intellij.rider.web.extensions.webBrowserDebugSupport) | WebBrowserDebugSupport |
com.intellij.rider.writingAccessProvider (https://jb.gg/ipe?extensions=com.intellij.rider.writingAccessProvider) | RiderDebugWritingAccessProvider |
com.intellij.rider.xaml.preview.editor (https://jb.gg/ipe?extensions=com.intellij.rider.xaml.preview.editor) | XamlPreviewEditorExtension |
com.intellij.solutionExplorerCustomization (https://jb.gg/ipe?extensions=com.intellij.solutionExplorerCustomization) | SolutionExplorerCustomization |
com.intellij.solutionExplorerRootProvider (https://jb.gg/ipe?extensions=com.intellij.solutionExplorerRootProvider) | SolutionExplorerRootProvider |
com.intellij.solutionManagerExtensions (https://jb.gg/ipe?extensions=com.intellij.solutionManagerExtensions) | SolutionManagerExtensions |
com.intellij.solutionViewPsiNodeNavigator (https://jb.gg/ipe?extensions=com.intellij.solutionViewPsiNodeNavigator) | SolutionViewPsiNodeNavigator |
Extension Point | Implementation |
|---|---|
com.intellij.rider.settings.machineDependentBackendSetting (https://jb.gg/ipe?extensions=com.intellij.rider.settings.machineDependentBackendSetting) | n/a |
Product-Specific Plugin Development: RubyMine (RubyMine Plugin Development)
80 Extension Points and 12 Listeners for RubyMine
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Extension Point | Implementation |
|---|---|
org.jetbrains.plugins.ruby.coverage.deserializer (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.coverage.deserializer) | RubyCoverageDeserializationProvider |
Extension Point | Implementation |
|---|---|
com.intellij.ruby.projectStructure.sourceRootEditHandler (https://jb.gg/ipe?extensions=com.intellij.ruby.projectStructure.sourceRootEditHandler) | RubyModuleSourceRootEditProvider |
org.jetbrains.plugins.ruby.breadcrumbsCustomizer (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.breadcrumbsCustomizer) | RubyBreadcrumbsCustomizer |
org.jetbrains.plugins.ruby.debug.infoProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.debug.infoProvider) | ContextInfoProvider |
org.jetbrains.plugins.ruby.gem.detector (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.gem.detector) | GemDetector |
org.jetbrains.plugins.ruby.gemFacetEditorTab (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.gemFacetEditorTab) | GemFacetEditorTabFactory |
org.jetbrains.plugins.ruby.generatorConfigurator (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.generatorConfigurator) | GeneratorActionConfigurator |
org.jetbrains.plugins.ruby.i18n.i18nProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.i18n.i18nProvider) | I18nProvider |
org.jetbrains.plugins.ruby.inflectionsProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.inflectionsProvider) | RubyInflectionsProvider |
org.jetbrains.plugins.ruby.methodTypeInfoProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.methodTypeInfoProvider) | RubyMethodTypeInfoProvider |
org.jetbrains.plugins.ruby.rails.assetsPathsProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rails.assetsPathsProvider) | SprocketAssetsPathProvider |
org.jetbrains.plugins.ruby.rails.sprocketsDirectiveContextProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rails.sprocketsDirectiveContextProvider) | SprocketsDirectiveContextProvider |
org.jetbrains.plugins.ruby.rails.viewFileTypesProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rails.viewFileTypesProvider) | RailsViewFileTypesProvider |
org.jetbrains.plugins.ruby.railsFacetEditorTab (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.railsFacetEditorTab) | FacetEditorTabFactory |
org.jetbrains.plugins.ruby.railsModelFieldsProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.railsModelFieldsProvider) | RailsModelFieldsProvider |
org.jetbrains.plugins.ruby.railsNavigateFrom (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.railsNavigateFrom) | RailsNavigateFromProvider |
org.jetbrains.plugins.ruby.railsSchemaParser (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.railsSchemaParser) | RailsSchemaParser |
org.jetbrains.plugins.ruby.rake.rakeRunCommandLineModifierProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rake.rakeRunCommandLineModifierProvider) | RakeRunCommandLineModifierProvider |
org.jetbrains.plugins.ruby.rake.runConfigurationSettingsFactory (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rake.runConfigurationSettingsFactory) | RakeRunConfigurationSettingsFactory |
org.jetbrains.plugins.ruby.rerunFailedTestsActionProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rerunFailedTestsActionProvider) | RubyRerunFailedTestsProvider |
org.jetbrains.plugins.ruby.ruby.associatedDeclarationProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.ruby.associatedDeclarationProvider) | RubyAssociatedDeclarationProvider |
org.jetbrains.plugins.ruby.ruby.run.configuration.debugger.rubyDebugHelperFactory (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.ruby.run.configuration.debugger.rubyDebugHelperFactory) | RubyDebugHelperFactory |
org.jetbrains.plugins.ruby.ruby.topLevelSymbolProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.ruby.topLevelSymbolProvider) | RubyTopLevelSymbolProvider |
org.jetbrains.plugins.ruby.ruby.typeSignatureProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.ruby.typeSignatureProvider) | RubyTypeSignatureProvider |
org.jetbrains.plugins.ruby.rubyFileStructureProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rubyFileStructureProvider) | RubyFileStructureViewProvider |
org.jetbrains.plugins.ruby.rubyInsertHandlerProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.rubyInsertHandlerProvider) | RubyInsertHandlerProvider |
org.jetbrains.plugins.ruby.runConfigurationExtension (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.runConfigurationExtension) | RubyRunConfigurationExtension |
org.jetbrains.plugins.ruby.structureViewCustomizer (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.structureViewCustomizer) | RubyStructureViewCustomizer |
org.jetbrains.plugins.ruby.testing.rspec.rspecContextNameProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.testing.rspec.rspecContextNameProvider) | RSpecContextNameProvider |
org.jetbrains.plugins.ruby.testing.rspec.rspecContextSymbolProvider (https://jb.gg/ipe?extensions=org.jetbrains.plugins.ruby.testing.rspec.rspecContextSymbolProvider) | RSpecContextSymbolProvider |
Extension Point | Implementation |
|---|---|
com.intellij.lang.ruby.rbs.containerHierarchyMapper (https://jb.gg/ipe?extensions=com.intellij.lang.ruby.rbs.containerHierarchyMapper) | RbsContainerHierarchyMapper |
Product-Specific Plugin Development: IntelliJ IDEA Ultimate (IntelliJ IDEA Ultimate)
Spring API Extension Points and Listeners are available in the Spring-related plugins, which are available in IntelliJ IDEA Ultimate only.
53 Extension Points and 5 Listeners for Spring API
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Topic | Listener |
|---|---|
SpringBootEndpointsTabSettings#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.spring.boot.run.lifecycle.tabs.SpringBootEndpointsTabSettings.Listener) | Listener |
SpringRepositoriesViewSettings#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.spring.data.commons.view.SpringRepositoriesViewSettings.Listener) | Listener |
SpringFileSetService#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.spring.facet.SpringFileSetService.SpringFileSetListener) | SpringFileSetListener |
SpringMvcViewSettings#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.spring.mvc.toolwindow.SpringMvcViewSettings.Listener) | Listener |
SpringBeansViewSettings#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.spring.toolWindow.SpringBeansViewSettings.Listener) | Listener |
Extension Point | Implementation |
|---|---|
com.intellij.spring.autodetected.filesets (https://jb.gg/ipe?extensions=com.intellij.spring.autodetected.filesets) | SpringAutodetectedFilesetsSearcher |
com.intellij.spring.autodetected.models (https://jb.gg/ipe?extensions=com.intellij.spring.autodetected.models) | SpringAutodetectedModelsSearcher |
com.intellij.spring.beanClassLineMarker (https://jb.gg/ipe?extensions=com.intellij.spring.beanClassLineMarker) | BeanClassLineMarker |
com.intellij.spring.beans.stereotype (https://jb.gg/ipe?extensions=com.intellij.spring.beans.stereotype) | SpringBeanStereotype |
com.intellij.spring.componentScanExtender (https://jb.gg/ipe?extensions=com.intellij.spring.componentScanExtender) | ComponentScanExtender |
com.intellij.spring.conditionalEvaluatorProvider (https://jb.gg/ipe?extensions=com.intellij.spring.conditionalEvaluatorProvider) | ConditionalEvaluatorProvider |
com.intellij.spring.configSearcherScopeModifier (https://jb.gg/ipe?extensions=com.intellij.spring.configSearcherScopeModifier) | ConfigSearcherScopeModifier |
com.intellij.spring.configurator (https://jb.gg/ipe?extensions=com.intellij.spring.configurator) | SpringConfigurator |
com.intellij.spring.customBeanScope (https://jb.gg/ipe?extensions=com.intellij.spring.customBeanScope) | SpringCustomBeanScope |
com.intellij.spring.customConverterProvider (https://jb.gg/ipe?extensions=com.intellij.spring.customConverterProvider) | Provider |
com.intellij.spring.customLocalComponentsDiscoverer (https://jb.gg/ipe?extensions=com.intellij.spring.customLocalComponentsDiscoverer) | CustomLocalComponentsDiscoverer |
com.intellij.spring.customModuleComponentsDiscoverer (https://jb.gg/ipe?extensions=com.intellij.spring.customModuleComponentsDiscoverer) | CustomModuleComponentsDiscoverer |
com.intellij.spring.customNamespaces (https://jb.gg/ipe?extensions=com.intellij.spring.customNamespaces) | SpringCustomNamespaces |
com.intellij.spring.effective.types.provider (https://jb.gg/ipe?extensions=com.intellij.spring.effective.types.provider) | SpringBeanEffectiveTypeProvider |
com.intellij.spring.factoryMethodTypeHandler (https://jb.gg/ipe?extensions=com.intellij.spring.factoryMethodTypeHandler) | CustomFactoryMethodTypeHandler |
com.intellij.spring.fileSetEditorCustomization (https://jb.gg/ipe?extensions=com.intellij.spring.fileSetEditorCustomization) | SpringFileSetEditorCustomization |
com.intellij.spring.inspectionsRegistryAdditionalFilesContributor (https://jb.gg/ipe?extensions=com.intellij.spring.inspectionsRegistryAdditionalFilesContributor) | AdditionalFilesContributor |
com.intellij.spring.inspectionsRegistryContributor (https://jb.gg/ipe?extensions=com.intellij.spring.inspectionsRegistryContributor) | Contributor |
com.intellij.spring.jam.customMetaImplementation (https://jb.gg/ipe?extensions=com.intellij.spring.jam.customMetaImplementation) | n/a |
com.intellij.spring.localAnnotationModelDependentModelsProvider (https://jb.gg/ipe?extensions=com.intellij.spring.localAnnotationModelDependentModelsProvider) | LocalAnnotationModelDependentModelsProvider |
com.intellij.spring.localModelProducer (https://jb.gg/ipe?extensions=com.intellij.spring.localModelProducer) | SpringLocalModelProducer |
com.intellij.spring.placeholderReferenceResolver (https://jb.gg/ipe?extensions=com.intellij.spring.placeholderReferenceResolver) | SpringPlaceholderReferenceResolver |
com.intellij.spring.resourceTypeProvider (https://jb.gg/ipe?extensions=com.intellij.spring.resourceTypeProvider) | SpringResourceTypeProvider |
com.intellij.spring.scriptBeanPsiClassDiscoverer (https://jb.gg/ipe?extensions=com.intellij.spring.scriptBeanPsiClassDiscoverer) | ScriptBeanPsiClassDiscoverer |
com.intellij.spring.settingsProvider (https://jb.gg/ipe?extensions=com.intellij.spring.settingsProvider) | SpringSettingsProvider |
com.intellij.spring.testingAnnotationsProvider (https://jb.gg/ipe?extensions=com.intellij.spring.testingAnnotationsProvider) | SpringTestingAnnotationsProvider |
com.intellij.spring.testingImplicitContextsProvider (https://jb.gg/ipe?extensions=com.intellij.spring.testingImplicitContextsProvider) | SpringTestingImplicitContextsProvider |
com.intellij.spring.valueConverter (https://jb.gg/ipe?extensions=com.intellij.spring.valueConverter) | SpringValueConvertersProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.boot.configFileDetector (https://jb.gg/ipe?extensions=com.intellij.spring.boot.configFileDetector) | SpringBootConfigFileDetector |
com.intellij.spring.boot.customHintReferenceProvider (https://jb.gg/ipe?extensions=com.intellij.spring.boot.customHintReferenceProvider) | SpringBootCustomHintReferenceProvider |
com.intellij.spring.boot.modelConditionalContributor (https://jb.gg/ipe?extensions=com.intellij.spring.boot.modelConditionalContributor) | ConditionalContributor |
com.intellij.spring.boot.modelConfigFileContributor (https://jb.gg/ipe?extensions=com.intellij.spring.boot.modelConfigFileContributor) | SpringBootModelConfigFileContributor |
com.intellij.spring.boot.modelConfigFileNameContributor (https://jb.gg/ipe?extensions=com.intellij.spring.boot.modelConfigFileNameContributor) | SpringBootModelConfigFileNameContributor |
com.intellij.spring.boot.modelExtender (https://jb.gg/ipe?extensions=com.intellij.spring.boot.modelExtender) | SpringBootModelExtender |
com.intellij.spring.boot.replacementTokenResolver (https://jb.gg/ipe?extensions=com.intellij.spring.boot.replacementTokenResolver) | SpringBootReplacementTokenResolver |
Extension Point | Implementation |
|---|---|
com.intellij.spring.boot.initializr.sharedIndexesProvider (https://jb.gg/ipe?extensions=com.intellij.spring.boot.initializr.sharedIndexesProvider) | SpringSharedIndexesProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.graphql.appPathProvider (https://jb.gg/ipe?extensions=com.intellij.spring.graphql.appPathProvider) | GraphQLApplicationPathProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.messaging.urlProvider (https://jb.gg/ipe?extensions=com.intellij.spring.messaging.urlProvider) | SpringMessagingUrlProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.mvc.applicationPathProvider (https://jb.gg/ipe?extensions=com.intellij.spring.mvc.applicationPathProvider) | SpringApplicationPathProvider |
com.intellij.spring.mvc.mergingMvcRequestMappingLineMarkerProvider (https://jb.gg/ipe?extensions=com.intellij.spring.mvc.mergingMvcRequestMappingLineMarkerProvider) | SpringMergingMvcRequestMappingLineMarkerProvider |
com.intellij.spring.mvc.springEndpointsIconProvider (https://jb.gg/ipe?extensions=com.intellij.spring.mvc.springEndpointsIconProvider) | SpringEndpointsIconProvider |
com.intellij.spring.mvc.viewResolverFactory (https://jb.gg/ipe?extensions=com.intellij.spring.mvc.viewResolverFactory) | ViewResolverFactory |
Extension Point | Implementation |
|---|---|
com.intellij.spring.security.rolesProvider (https://jb.gg/ipe?extensions=com.intellij.spring.security.rolesProvider) | SpringSecurityRolesProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.boot.mvc.templateAvailabilityProvider (https://jb.gg/ipe?extensions=com.intellij.spring.boot.mvc.templateAvailabilityProvider) | TemplateAvailabilityProvider |
Extension Point | Implementation |
|---|---|
com.intellij.spring.boot.run.applicationUpdatePolicy (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.applicationUpdatePolicy) | SpringBootApplicationUpdatePolicy |
com.intellij.spring.boot.run.applicationUrlPathProviderFactory (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.applicationUrlPathProviderFactory) | SpringBootApplicationUrlPathProviderFactory |
com.intellij.spring.boot.run.endpoint (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.endpoint) | Endpoint |
com.intellij.spring.boot.run.endpointTabConfigurable (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.endpointTabConfigurable) | EndpointTabConfigurable |
com.intellij.spring.boot.run.liveBeansPanelContent (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.liveBeansPanelContent) | LiveBeansPanelContent |
com.intellij.spring.boot.run.starterManager (https://jb.gg/ipe?extensions=com.intellij.spring.boot.run.starterManager) | SpringBootStarterManager |
Extension Point | Implementation |
|---|---|
com.intellij.spring.el.contexts (https://jb.gg/ipe?extensions=com.intellij.spring.el.contexts) | SpringElContextsExtension |
com.intellij.spring.el.injection.context (https://jb.gg/ipe?extensions=com.intellij.spring.el.injection.context) | SpringElInjectionContext |
Extension Point | Implementation |
|---|---|
com.intellij.spring.gutterDiagramActionProvider (https://jb.gg/ipe?extensions=com.intellij.spring.gutterDiagramActionProvider) | SpringGutterDiagramActionProvider |
Product-Specific Plugin Development: WebStorm (WebStorm Plugin Development)
74 Extension Points and 5 Listeners for WebStorm
See IntelliJ Platform Extension Point and Listener List for IntelliJ Platform and Open Source Plugins Extension Point and Listener List for additional plugins.
See also Explore the IntelliJ Platform API for more information and strategies.
Topic searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Listener links corresponding listener to implement.
See Listeners on how to register listeners.
Extension Point searches for usages inside existing implementations of open-source IntelliJ Platform plugins via IntelliJ Platform Explorer (https://jb.gg/ipe).
Implementation is the related Extension Point class.
See Extensions on how to declare extensions in your plugin.
See Verifying Plugin Compatibility for overview of API status.
Icon | Description | Details |
|---|---|---|
Deprecated API | Please see code documentation for replacement | |
Scheduled for Removal API | Please see code documentation for replacement | |
Obsolete API | Do not use in new code, please see code documentation for replacement ("Obsolete API" in "Verifying Plugin Compatibility") | |
Experimental API | Annotated with ApiStatus.@Experimental (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), API might be altered or removed without prior notice | |
Internal API | Annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), must not be used by 3rd party, see Internal API Migration | |
Project-Level Extension Point/Topic | Can have Project (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/Project.java) as constructor parameter Extension Point: Declared with area="IDEA_PROJECT" Listener: registered in <projectListeners> ("projectListeners" in "Plugin Configuration File") | |
Non-Dynamic Extension Point | Installation/update of plugin requires IDE restart (Dynamic Plugins) | |
DumbAware Extension Point | Implementations marked with DumbAware (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/project/DumbAware.java) will be processed during dumb mode ("Dumb Mode" in "Indexing and PSI Stubs") |
Topic | Listener |
|---|---|
JestConsoleProperties#COVERAGE_CONFIG_TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.javascript.jest.JestCoverageConfigListener) | JestCoverageConfigListener |
PackageJsonFileManager#CHANGES_TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.javascript.nodejs.packageJson.PackageJsonFileManager.PackageJsonChangesListener) | PackageJsonChangesListener |
VitestConsoleProperties#COVERAGE_CONFIG_TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.javascript.testing.vitest.coverage.VitestCoverageConfigListener) | VitestCoverageConfigListener |
JSLibraryManager#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.lang.javascript.library.JSLibraryManager.JSLibraryManagerChangeListener) | JSLibraryManagerChangeListener |
JSRemoteModulesRegistry#TOPIC (https://jb.gg/ipe/listeners?topics=com.intellij.lang.javascript.modules.remote.JSRemoteModulesChangeListener) | JSRemoteModulesChangeListener |
Extension Point | Implementation |
|---|---|
com.intellij.javascript.module.provider (https://jb.gg/ipe?extensions=com.intellij.javascript.module.provider) | JSModuleConnectionProvider |
Extension Point | Implementation |
|---|---|
com.intellij.javascript.webTypes (https://jb.gg/ipe?extensions=com.intellij.javascript.webTypes) | n/a |
Extension Point | Implementation |
|---|---|
JavaScript.FlowJSAnnotatorCheckerProvider (https://jb.gg/ipe?extensions=JavaScript.FlowJSAnnotatorCheckerProvider) | FlowJSAnnotatorCheckerProvider |
JavaScript.JSImplicitElementsIndexFileTypeProvider (https://jb.gg/ipe?extensions=JavaScript.JSImplicitElementsIndexFileTypeProvider) | JSImplicitElementsIndexFileTypeProvider |
JavaScript.TypeScriptAnnotatorCheckerProvider (https://jb.gg/ipe?extensions=JavaScript.TypeScriptAnnotatorCheckerProvider) | TypeScriptAnnotatorCheckerProvider |
JavaScript.analysisHandlersFactory (https://jb.gg/ipe?extensions=JavaScript.analysisHandlersFactory) | JSAnalysisHandlersFactory |
JavaScript.classInheritorsProvider (https://jb.gg/ipe?extensions=JavaScript.classInheritorsProvider) | JSClassInheritorsProvider |
JavaScript.completionHelper (https://jb.gg/ipe?extensions=JavaScript.completionHelper) | JSCompletionHelper |
JavaScript.completionPlaceFilter (https://jb.gg/ipe?extensions=JavaScript.completionPlaceFilter) | JSCompletionPlaceFilterProvider |
JavaScript.componentUsageProvider (https://jb.gg/ipe?extensions=JavaScript.componentUsageProvider) | JSComponentUsageProvider |
JavaScript.conditionalCompilationDefinitionsProvider (https://jb.gg/ipe?extensions=JavaScript.conditionalCompilationDefinitionsProvider) | JSConditionalCompilationDefinitionsProvider |
JavaScript.dialectSpecificHandlersFactory (https://jb.gg/ipe?extensions=JavaScript.dialectSpecificHandlersFactory) | JSDialectSpecificHandlersFactory |
JavaScript.elementScopeProvider (https://jb.gg/ipe?extensions=JavaScript.elementScopeProvider) | JSElementResolveScopeProvider |
JavaScript.frameworkIndexingHandler (https://jb.gg/ipe?extensions=JavaScript.frameworkIndexingHandler) | FrameworkIndexingHandler |
JavaScript.frameworkSpecificHandler (https://jb.gg/ipe?extensions=JavaScript.frameworkSpecificHandler) | JSFrameworkSpecificHandler |
JavaScript.frameworkSpecificStructureViewExtension (https://jb.gg/ipe?extensions=JavaScript.frameworkSpecificStructureViewExtension) | JSFrameworkSpecificStructureExtension |
JavaScript.handlersFactory (https://jb.gg/ipe?extensions=JavaScript.handlersFactory) | JSHandlersFactory |
JavaScript.iconProvider (https://jb.gg/ipe?extensions=JavaScript.iconProvider) | JSIconProvider |
JavaScript.importCandidatesFactory (https://jb.gg/ipe?extensions=JavaScript.importCandidatesFactory) | CandidatesFactory |
JavaScript.importCandidatesFilterFactory (https://jb.gg/ipe?extensions=JavaScript.importCandidatesFilterFactory) | FilterFactory |
JavaScript.importModulePathStrategy (https://jb.gg/ipe?extensions=JavaScript.importModulePathStrategy) | JSImportModulePathStrategy |
JavaScript.indexedFileTypeProvider (https://jb.gg/ipe?extensions=JavaScript.indexedFileTypeProvider) | IndexedFileTypeProvider |
JavaScript.inheritedLanguagesConfigurableProvider (https://jb.gg/ipe?extensions=JavaScript.inheritedLanguagesConfigurableProvider) | JSInheritedLanguagesConfigurableProvider |
JavaScript.intentionAndInspectionFilter (https://jb.gg/ipe?extensions=JavaScript.intentionAndInspectionFilter) | IntentionAndInspectionFilter |
JavaScript.isNotMinifiedFile.provider (https://jb.gg/ipe?extensions=JavaScript.isNotMinifiedFile.provider) | JSIsNotMinifiedFileProvider |
JavaScript.jestPackageProvider (https://jb.gg/ipe?extensions=JavaScript.jestPackageProvider) | JestPackageProvider |
JavaScript.jsDocCustomTagsHandler (https://jb.gg/ipe?extensions=JavaScript.jsDocCustomTagsHandler) | JSDocCustomTagsHandler |
JavaScript.jsxImplementation (https://jb.gg/ipe?extensions=JavaScript.jsxImplementation) | JSXImplementation |
JavaScript.lang.templates (https://jb.gg/ipe?extensions=JavaScript.lang.templates) | |
JavaScript.languageServiceProvider (https://jb.gg/ipe?extensions=JavaScript.languageServiceProvider) | JSLanguageServiceProvider |
JavaScript.languageServiceRemoteHelperFactory (https://jb.gg/ipe?extensions=JavaScript.languageServiceRemoteHelperFactory) | Factory |
JavaScript.moduleExportsProvider (https://jb.gg/ipe?extensions=JavaScript.moduleExportsProvider) | JSModuleExportsProvider |
JavaScript.moduleReferenceContributor (https://jb.gg/ipe?extensions=JavaScript.moduleReferenceContributor) | JSModuleReferenceContributor |
JavaScript.nodeModulesIndexableFileNamesProvider (https://jb.gg/ipe?extensions=JavaScript.nodeModulesIndexableFileNamesProvider) | NodeModulesIndexableFileNamesProvider |
JavaScript.nodeRunConfigurationExtension (https://jb.gg/ipe?extensions=JavaScript.nodeRunConfigurationExtension) | AbstractNodeRunConfigurationExtension |
JavaScript.predefinedLibraryProvider (https://jb.gg/ipe?extensions=JavaScript.predefinedLibraryProvider) | JSPredefinedLibraryProvider |
JavaScript.projectGeneratorPanelCustomizer (https://jb.gg/ipe?extensions=JavaScript.projectGeneratorPanelCustomizer) | ProjectGeneratorSettingsCustomizer |
JavaScript.resolveHelper (https://jb.gg/ipe?extensions=JavaScript.resolveHelper) | JSResolveHelper |
JavaScript.runConfigurationBuilder (https://jb.gg/ipe?extensions=JavaScript.runConfigurationBuilder) | JSRunConfigurationBuilder |
JavaScript.scanningFileListenerContributor (https://jb.gg/ipe?extensions=JavaScript.scanningFileListenerContributor) | ScanningFileListenerContributor |
JavaScript.smartCompletionContributor (https://jb.gg/ipe?extensions=JavaScript.smartCompletionContributor) | JSSmartCompletionContributor |
JavaScript.spellcheckerProvider (https://jb.gg/ipe?extensions=JavaScript.spellcheckerProvider) | JSSpellcheckerProvider |
JavaScript.testWatchProvider (https://jb.gg/ipe?extensions=JavaScript.testWatchProvider) | JsTestWatchProvider |
JavaScript.tsConfigCustomizer (https://jb.gg/ipe?extensions=JavaScript.tsConfigCustomizer) | TypeScriptConfigCustomizer |
JavaScript.tsImportResolver (https://jb.gg/ipe?extensions=JavaScript.tsImportResolver) | TypeScriptImportsResolverProvider |
JavaScript.tsServiceExtension (https://jb.gg/ipe?extensions=JavaScript.tsServiceExtension) | TypeScriptServiceExtension |
JavaScript.unresolvedReferenceErrorUpdater (https://jb.gg/ipe?extensions=JavaScript.unresolvedReferenceErrorUpdater) | JSUnresolvedReferenceErrorUpdater |
JavaScript.webBundlerCssReferenceContributor (https://jb.gg/ipe?extensions=JavaScript.webBundlerCssReferenceContributor) | JSModuleReferenceContributor |
JavaScript.webBundlerDefinition (https://jb.gg/ipe?extensions=JavaScript.webBundlerDefinition) | WebBundlerDefinition |
JavaScript.xmlBackedClassProvider (https://jb.gg/ipe?extensions=JavaScript.xmlBackedClassProvider) | XmlBackedJSClassProvider |
NodeJS.runConfigurationLocationFilter (https://jb.gg/ipe?extensions=NodeJS.runConfigurationLocationFilter) | NodeRunConfigurationLocationFilter |
com.intellij.JavaScript.linter.descriptor (https://jb.gg/ipe?extensions=com.intellij.JavaScript.linter.descriptor) | JSLinterDescriptor |
com.intellij.JavaScript.linter.execution.suppressor (https://jb.gg/ipe?extensions=com.intellij.JavaScript.linter.execution.suppressor) | JSLinterExecutionSuppressor |
com.intellij.eslint.ruleMappersFactory (https://jb.gg/ipe?extensions=com.intellij.eslint.ruleMappersFactory) | EslintRuleMappersFactory |
com.intellij.javascript.extract.interface.extension (https://jb.gg/ipe?extensions=com.intellij.javascript.extract.interface.extension) | JSCustomExtractInterfaceHandler |
com.intellij.javascript.introduce.variable.extension (https://jb.gg/ipe?extensions=com.intellij.javascript.introduce.variable.extension) | JSCustomIntroduceVariableHandler |
com.intellij.javascript.json.schema.provider (https://jb.gg/ipe?extensions=com.intellij.javascript.json.schema.provider) | JsonSchemaInJavaScriptProvider |
com.intellij.javascript.library.externalDefinitionsContributor (https://jb.gg/ipe?extensions=com.intellij.javascript.library.externalDefinitionsContributor) | TypeScriptExternalDefinitionsContributor |
com.intellij.javascript.names.suggester (https://jb.gg/ipe?extensions=com.intellij.javascript.names.suggester) | JSNamesSuggester |
com.intellij.javascript.rename.extension (https://jb.gg/ipe?extensions=com.intellij.javascript.rename.extension) | JSRenameExtension |
com.intellij.jsbtFileManagerProvider (https://jb.gg/ipe?extensions=com.intellij.jsbtFileManagerProvider) | JsbtFileManagerProvider |
com.intellij.jsbtService (https://jb.gg/ipe?extensions=com.intellij.jsbtService) | JsbtApplicationService |
Extension Point | Implementation |
|---|---|
org.jetbrains.plugins.node-remote-interpreter.nodeRemoteTargetRunSetupFactory (https://jb.gg/ipe?extensions=org.jetbrains.plugins.node-remote-interpreter.nodeRemoteTargetRunSetupFactory) | NodeRemoteTargetRunSetupFactory |
Extension Point | Implementation |
|---|---|
com.intellij.sass.extension (https://jb.gg/ipe?extensions=com.intellij.sass.extension) | SassExtension |
The following links represent useful resources for working with the IntelliJ Platform and creating plugins.
Use Writerside (https://www.jetbrains.com/writerside/) to create and publish documentation for your plugin.
IDE Settings, Caches, Logs, and Plugins (https://intellij-support.jetbrains.com/hc/en-us/articles/206544519-Directories-used-by-the-IDE-to-store-settings-caches-plugins-and-logs)
IntelliJ Plugin Verifier (https://github.com/JetBrains/intellij-plugin-verifier) (see also Verifying Plugin Compatibility)
IntelliJ Platform Explorer (https://jb.gg/ipe) (Blog post (https://blog.jetbrains.com/platform/2020/12/intellij-platform-explorer-get-to-the-extension-point/))
IntelliJ Log Analyzer (https://github.com/JetBrains/IntelliJ-Log-Analyzer)
Figma UI Kit (https://jetbrains.design/intellij/resources/UI_kit/) (Blog post (https://blog.jetbrains.com/idea/2021/05/intellij-platform-ui-kit/))
PsiViewer (https://plugins.jetbrains.com/plugin/227-psiviewer)
Grammar-Kit (https://plugins.jetbrains.com/plugin/6606-grammar-kit) & Grammar-Kit Folder (https://plugins.jetbrains.com/plugin/12983-grammar-kit-folder)
Index Viewer (https://plugins.jetbrains.com/plugin/13029-index-viewer/)
IDE Perf (https://plugins.jetbrains.com/plugin/15104-ide-perf)
Thread Access Info (https://plugins.jetbrains.com/plugin/16815-thread-access-info)
IntelliJ Platform SDK Documentation (https://github.com/JetBrains/intellij-sdk-docs) & IntelliJ Platform SDK Code Samples (https://github.com/JetBrains/intellij-sdk-code-samples)
IntelliJ Platform Plugin Template (https://github.com/JetBrains/intellij-platform-plugin-template)
IntelliJ Community Edition (https://github.com/JetBrains/intellij-community)
IntelliJ Plugins (https://github.com/JetBrains/intellij-plugins)
Open Source Plugins (https://plugins.jetbrains.com/search?orderBy=name&shouldHaveSource=true)
Webinars have moved ("Webinars" in "Learning Resources").
Staying up to dateSubscribe to Marketplace Developer News (https://jb.gg/mp-updates) to receive news and announcements. Also follow JBPlatform (https://x.com/JBPlatform/) on X (formerly Twitter) and visit JetBrains Platform Blog (https://blog.jetbrains.com/platform/) and JetBrains Marketplace on LinkedIn (https://www.linkedin.com/showcase/jetbrains-marketplace/).
WebStorm Under the Hood: How We Added Astro Support to Our IDE (https://blog.jetbrains.com/webstorm/2023/08/webstorm-under-the-hood-how-we-added-astro-support-to-our-ide/) 07/2023
The Plugin Obfuscation Experience (https://blog.jetbrains.com/platform/2022/05/the-plugin-obfuscation-experience/) 05/2022
Themes in IntelliJ-based IDEs (https://blog.jetbrains.com/platform/2021/10/themes-in-intellij-based-ides/) 10/2021
The Revamping Plugins series is about sharing the experience of updating outdated plugins to align with the latest IntelliJ Platform SDK guidelines.
Revamping Plugins #1 – .ignore (https://blog.jetbrains.com/platform/2021/01/revamping-plugins-ignore/) 01/2021
Revamping Plugins #2 – IDEA Feature Suggester (https://blog.jetbrains.com/platform/2021/03/revamping-plugins-2-idea-feature-suggester/) 03/2021
Revamping Plugins #3 – Migrating from DevKit to the Gradle build system (https://blog.jetbrains.com/platform/2021/12/migrating-from-devkit-to-the-gradle-build-system/) 12/2021
A tutorial blog post series for JavaScript developers.
Webinar | Info |
|---|---|
How To Build a Plugin for JetBrains IDEs (Analog.js Example) Piotr Tomiak & guests, 02/2024 | Walking you through the first steps of building a community plugin for Analog.js |
Busy Plugin Developers #7 Natalia Melnikova, 11/2023 | How to Upload a Plugin and Get It Approved on JetBrains Marketplace |
Busy Plugin Developers #6 Dmitry Tseyler, 05/2023 | Polaris plugin for IntelliJ Platform code search (no longer available) |
Busy Plugin Developers #5 Matthias Koch, 09/2022 | Building Extensions for Rider and ReSharper |
Busy Plugin Developers #4 Dmitry Kandalov/Yann Cebron, 04/2022 | Adding IDE Features at runtime using LivePlugin |
Busy Plugin Developers #3 Jakub Chrzanowski/Anna Maltceva/Yann Cebron, 11/2021 | Building Themes for IntelliJ-based IDEs Latest updates to JetBrains Marketplace |
Busy Plugin Developers #2 Anna Maltceva/Łukasz Wawrzyk/Jakub Chrzanowski, 09/2021 | How to improve your Marketplace plugin page to attract more users ide-probe, a testing and benchmarking framework for IntelliJ-based IDEs |
Busy Plugin Developers #1 Jakub Chrzanowski/Semyon Atamas/Paweł Lipski, 07/2021 | What is Gradle IntelliJ Plugin New features of Gradle IntelliJ Plugin How to start with Gradle IntelliJ Plugin Types of signing and how they work How JetBrains Marketplace signature works How to sign your plugin Tools and tips for testing UI of IntelliJ Plugins |
Make IntelliJ IDEA Your Own Sirisha Pratha, 05/2021 | In this session, we'll explore the benefits provided by IntelliJ IDEA's customization options, and you will learn how to make the IDE your own. We will also look at the plugins available on the JetBrains Marketplace, discuss the process of developing them, and demo a few custom ones. |
IntelliJ IDEA Conf 2021 Various Speakers, 02/2021 | Playlist includes a number of presentations related to plugin development |
Busy Plugin Developers #0 Mikhail Vink/Jakub Chrzanowski/Yann Cebron, 12/2020 | IntelliJ Platform Plugin Template Plugin DevKit Features IntelliJ Platform Explorer What's coming in 2021? Introduction to the Marketplace How to make your plugin successful? Sell on the Marketplace |
How We Built Comma, the Raku IDE, on the IntelliJ Platform Jonathan Worthington, 01/2020 | How to build custom language support How to go from a language support plugin to an IDE Lessons Learned |
Building IntelliJ IDEA plugins in Scala Igal Tabachnik, 2020 | In this talk, I will show how to create plugins for IDEA from scratch in Scala, and show why Scala's unique features make it a great fit for such tasks as inspecting and manipulating Abstract Syntax Trees (ASTs) to create your own custom suggestions and quick fixes. |
Live Development of a PyCharm Plugin Joachim Ansorg, 2019 | Background and architecture of IntelliJ plugins Development using tests Implementing interesting extension points Viewing the plugin in the IDE (PyCharm) |
Build Developer Tools On Top of IntelliJ Platform Dmitry Jemerov, 2013 | This webinar makes an overview on IntelliJ Platform and explains how you can use it for building your own products. |
Please make sure to follow the guidelines from Plugin Overview page (https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html) for an optimal presentation of your plugin on JetBrains Marketplace. The Busy Plugin Developers. Episode 2 discusses 5 tips for optimizing JetBrains Marketplace plugin page (https://youtu.be/oB1GA9JeeiY?t=52) in more detail.
Plugin vendors can also submit a blog post for the JetBrains Platform blog, please see Contributing to the Blog (https://plugins.jetbrains.com/docs/marketplace/contributing-to-the-blog.html) for details.
JetBrains Marketplace (https://plugins.jetbrains.com) provides embeddable widgets that you can place on your website using a simple code snippet:
Embeddable Plugin Card - renders an information card with the plugin name, icon, description, last update, and download counter,
Embeddable Install Plugin Button - provides a button that allows installing your plugin right in the user's IDE, if it is currently open.
For more details, please follow the Embeddable Content (https://plugins.jetbrains.com/docs/marketplace/embeddable-content.html) section in the JetBrains Marketplace documentation.
Adding badges to the README files in open-source projects is common for providing additional information for users.
Below are listed a few badges related to the IntelliJ SDK and plugins development provided by shields.io (https://shields.io):
The following code snippets contain :pluginId and :packageName placeholders.
:pluginId can be obtained from your plugin page URL, like: https://plugins.jetbrains.com/plugin/6954-kotlin - in this case, it's 6954.
:pluginId also accepts a string ID that can be found in Versions tab, like https://plugins.jetbrains.com/plugin/6954-kotlin/versions.
:packageName for ReSharper accepts only string ID.
IntelliJ Plugins
ReSharper Plugins
IntelliJ Plugin Numeric Rating
IntelliJ Plugins
ReSharper Plugins
ReSharper Plugins (incl. pre-releases)
GitHub Actions Workflow
JetBrains IntelliJ Platform SDK Docs
https://plugins.jetbrains.com/docs/intellij
[](https://plugins.jetbrains.com/docs/intellij)JetBrains Platform Slack
https://plugins.jetbrains.com/slack
[](https://plugins.jetbrains.com/slack)@JBPlatform on X (formerly Twitter)
[](https://x.com/JBPlatform)The following independent companies and individuals provide paid plugin consulting and development services.
Also, consider posting requests in #jobs channel on JetBrains Platform Slack (https://plugins.jetbrains.com/slack).
JetBrains is not responsible for nor guarantees the performance of these independent third-party companies.
Name | Contact | Notes |
|---|---|---|
Joachim Ansorg | www.plugin-dev.com (https://www.plugin-dev.com) | -/- |
Daniel Espendiller | daniel@espendiller.net (mailto:daniel@espendiller.net) | -/- |
Robert Ekendahl | www.edaphic.studio (https://www.edaphic.studio) | Custom Language support |
Edument | jonathan@edument.cz (mailto:jonathan@edument.cz), edument.se (https://www.edument.se/en/page/intellij-platform-development) | Developers of Comma IDE (https://commaide.com) |
Mikhail Bolotov | mikhail.bolotov@gmail.com (https://plugins.jetbrains.com/organization/mbolotov) | Plugins: Cypress Support Pro (https://plugins.jetbrains.com/plugin/13987-cypress-support-pro), k6 (https://plugins.jetbrains.com/plugin/16141-k6) |
Ilscipio GmbH | Paul Piper (mailto:info@ilscipio.com), www.ilscipio.com (https://www.ilscipio.com) | Custom Plugin Development |
Tamás Balog | www.picimako.com (https://www.picimako.com) | Custom Plugin Development |
Please submit a PR or file a YouTrack issue (Getting Help) for changes or additions to this list.
IDE Provisioner (https://www.jetbrains.com/ide-services/ide-provisioner/) (currently in Early Access) comes with a local built-in repository of IntelliJ Platform plugins that allows you to choose specific plugins to be approved within your organization. It also lets you upload and distribute your own plugins inside your company, as well as any publicly available plugin from the internet.
If you intend to use a plugin repository other than the JetBrains Marketplace (JetBrains Marketplace), you will need to:
Create and maintain an updatePlugins.xml* file on the HTTPS web server you are using for your custom repository. This file describes all the plugins available in your custom repository and each plugin's download URL.
Upload your plugin JAR/ZIP file to an HTTPS web server. This can be the same web server you are using for the custom repository or a different HTTPS web server.
Add the URL for the custom repository to the JetBrains IDE Repository Settings (https://www.jetbrains.com/help/idea/managing-plugins.html#repos).
* The updatePlugin.xml file name is not fixed and can be different.
Gradle plugin IntelliJ plugin uploader (https://github.com/brian-mcnamara/plugin_uploader) can be used to automate deployment.
To avoid collisions between private plugins and those hosted on JetBrains Marketplace, an organization can reserve plugin IDs (https://plugins.jetbrains.com/docs/marketplace/reserved-plugin-ids.html).
To provide custom authentication, implement PluginRepositoryAuthProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/plugins/auth/PluginRepositoryAuthProvider.java) registered in com.intellij.pluginRepositoryAuthProvider extension point.
Every custom plugin repository must have at least one updatePlugins.xml file to describe every hosted plugin's latest available version. The description in updatePlugins.xml is used by JetBrains IDEs to locate plugins by attributes such as identifier, IDE version, and plugin version. These attributes are displayed by JetBrains IDEs to help users select or upgrade plugins. The description also tells the JetBrains IDE where to download the plugin itself.
A custom plugin repository's updatePlugins.xml file is constructed and maintained by the repository administrator. More than one updatePlugins.xml file may be required if the custom repository consumers are using more than one version of a JetBrains IDE. For example, updatePlugins-182.xml, updatePlugins-183.xml for IntelliJ IDEA 2018.2 and 2018.3, respectively. Each updatePlugins-*.xml file will have a unique URL that is added to the JetBrains IDE Repository Settings (https://www.jetbrains.com/help/idea/managing-plugins.html#repos).
The format of an updatePlugins.xml file is simply a list of sequential elements that describe each plugin:
<?xml version="1.0" encoding="UTF-8"?>
<!--
The <plugins> element (required) contains the description of the plugins
available at this repository.
-->
<plugins>
<!--
Each <plugin> element (required) describes one plugin in the repository.
Attributes:
- "id" (required) - used by JetBrains IDEs to uniquely identify
a plugin. Must match <id> in the plugin.xml file.
- "url" (required) - URL to download the plugin JAR/ZIP file.
Must be HTTPS.
- "version" (required) - version of this plugin. Must match <version>
in the plugin.xml file.
-->
<plugin
id="fully.qualified.id.of.this.plugin"
url="https://mycompany.example.com/my_repo/my_plugin.jar"
version="major.minor.update">
<!--
The <idea-version> element (required) must match the same element
in the plugin.xml file.
-->
<idea-version since-build="181.3" until-build="191.*"/>
</plugin>
<plugin
id="id.of.different.plugin"
url="https://othercompany.example.com/other_repo/other_plugin.jar"
version="major.minor">
<idea-version since-build="181.3" until-build="191.*"/>
</plugin>
<!-- And so on for other plugins... -->
</plugins>Notes:
An updatePlugins.xml file must contain at least one <plugin> element.
A plugin id may be listed only once in an updatePlugins.xml file.
Multiple plugins with the same id but different idea-version attributes must be split into separate updatePlugins-*.xml files. The requesting IDE's version is passed as build parameter and can be used for server-side filtering.
During plugin installation, the IDE reads the plugin JAR/ZIP file and thereafter displays more information about the plugin. In some cases, additional information included in updatePlugins.xml might help a user select a plugin when browsing the custom plugin repository (https://www.jetbrains.com/help/idea/managing-plugins.html#repos) before installation. The decision on what elements should be included in the file depends on the plugins and repository consumers. It is recommended to avoid adding unnecessary elements as they will have to be synchronized with each plugin's plugin.xml (Plugin Configuration File) file.
The additional candidate elements:
Element | Effects & Requirements |
|---|---|
| By default, the name of the plugin JAR/ZIP file is displayed before installation. Using the <name> element displays the name of the plugin. Contents should match the <name> ("name" in "Plugin Configuration File") element contents in the plugin's plugin.xml file to avoid confusion. |
| By default, no description for the plugin is displayed before installation. Using the <description> element will cause a description to be displayed before installation. Contents should match the <description> ("description" in "Plugin Configuration File") element contents in the plugin's plugin.xml file to avoid confusion. Optionally, an enclosing <![CDATA[ ]]> element can be used if the description needs to contain HTML tags. |
| By default, no change notes for the plugin are displayed before installation. Using the <change-notes> element will cause a description of changes to be displayed before installation. Contents should match the <change-notes> ("change-notes" in "Plugin Configuration File") element contents in the plugin's plugin.xml file to avoid confusion. Optionally, an enclosing <![CDATA[ ]]> element can be used if the change notes need to contain HTML tags. |
| Adding a plugin dependency in the <depends> element will cause asking a user about enabling the dependency plugin, if it is installed and disabled in the IDE. A plugin can specify multiple <depends> elements. A plugin entry should include only the dependencies on other plugins that are defined by the <depends> ("depends" in "Plugin Configuration File") elements in the plugin's plugin.xml file. The optional and config-file attributes are ignored and shouldn't be specified. |
JetBrains provides an official plugin repository JetBrains Marketplace (https://plugins.jetbrains.com) for all IntelliJ Platform-based IDEs, as well as other products.
These pages have moved to JetBrains Marketplace Documentation (https://plugins.jetbrains.com/docs/marketplace). Please update your bookmarks.
For your convenience, pages previously part of this documentation are linked below:
API Reference (https://plugins.jetbrains.com/docs/marketplace/api-reference.html)
Plugin Upload (https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html)
Plugins List (https://plugins.jetbrains.com/docs/marketplace/plugins-list.html)
Plugin Details (https://plugins.jetbrains.com/docs/marketplace/plugin-details.html)
Plugin Update Download (https://plugins.jetbrains.com/docs/marketplace/plugin-update-download.html)
Maven Interface (https://plugins.jetbrains.com/docs/marketplace/maven-interface.html)
Plugin Developers List (https://plugins.jetbrains.com/docs/marketplace/plugin-developers-list.html)
Plugin Recommendations (https://plugins.jetbrains.com/docs/marketplace/intellij-plugin-recommendations.html)
Custom Release Channels (https://plugins.jetbrains.com/docs/marketplace/custom-release-channels.html)
Please see Incompatible Changes in IntelliJ Platform and Plugins API for known breaking changes.
For API annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), see Internal API Migration for more details and replacements.
Compatibility with newer IDEs can easily be verified for plugins hosted on the JetBrains Marketplace (https://plugins.jetbrains.com) using the built-in Plugin Verifier (https://blog.jetbrains.com/platform/2018/07/plugins-repository-now-integrates-with-the-plugin-verification-tool/).
Integration in Gradle build (Configuring Gradle IntelliJ Plugin) is available using the runPluginVerifier ("runPluginVerifier" in "Gradle IntelliJ Plugin") task, please see Gradle IntelliJ Plugin - Plugin Verifier ("runPluginVerifier" in "Gradle IntelliJ Plugin") for details.
Navigation in the IDEReported places are highlighted and linked to the plugin's source code in the Gradle toolwindow output (2023.3).
You can easily integrate it in Continuous Integration (CI) environments by running this task as another quality check step. Check the IntelliJ Platform Plugin Template GitHub workflow configuration file (https://github.com/JetBrains/intellij-platform-plugin-template/blob/main/.github/workflows/build.yml) as sample.
If your plugin is hosted on GitHub and you are not using Gradle, consider using third-party GitHub Actions IntelliJ Platform Plugin Verifier (https://github.com/marketplace/actions/intellij-platform-plugin-verifier) or IntelliJ Plugin Verifier (https://github.com/marketplace/actions/intellij-plugin-verifier).
In other cases, intellij-plugin-verifier (https://github.com/JetBrains/intellij-plugin-verifier) can be used standalone as well.
The status of an API is marked using various annotations defined in ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java), please see their doc for more details. Use highlighting available via dedicated IDE inspections (https://www.jetbrains.com/help/idea/code-inspection.html) as noted below to prevent problems as early as possible.
ApiStatus.@Experimental is considered unstable and may break or be removed.
ApiStatus.@Internal must not be used by plugins, see Internal API Migration for more details and replacements.
ApiStatus.@ScheduledForRemoval denotes API that will be removed in a future version.
Inspection: JVM languages | Unstable API Usage and JVM languages | Unstable type is used in signature
API annotated with ApiStatus.@Obsolete has been replaced with a better alternative and must not be used for new code.
Inspection: Plugin DevKit | Code | Usages of ApiStatus.@Obsolete (2023.1)
API annotated with ApiStatus.@NonExtendable must not be extended, implemented or overridden.
Inspection: JVM languages | Class, interface, or method should not be extended
API annotated with ApiStatus.@OverrideOnly must not be called directly by the client.
Inspection: JVM languages | Method can only be overridden
Usage of Extension Points (Extensions) which are deprecated or annotated with ApiStatus.@Experimental or ApiStatus.@Internal is also highlighted in plugin.xml (Plugin Configuration File) files.
Inspection: Plugin DevKit | Plugin descriptor | Plugin.xml validity
A plugin might specify a compatibility range (Build Number Ranges) including releases where some API is not available. Under the hood, it uses an artifact containing generated data via ApiStatus.@AvailableSince, which is automatically attached to the project.
If values are not specified directly in plugin.xml (Plugin Configuration File) (e.g., when providing values via "patchPluginXml" in "Gradle IntelliJ Plugin" Gradle task), they must be set explicitly in the inspection's settings.
Inspection: Plugin DevKit | Code | Usage of IntelliJ API not available in older IDEs
IntelliJ API may be occasionally changed between releases, leading to existing plugins' incompatibilities with newer IDE builds.
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Staying up to dateSubscribe to Marketplace Developer News (https://jb.gg/mp-updates) to receive news and announcements. Also follow JBPlatform (https://x.com/JBPlatform/) on X (formerly Twitter) and visit JetBrains Platform Blog (https://blog.jetbrains.com/platform/) and JetBrains Marketplace on LinkedIn (https://www.linkedin.com/showcase/jetbrains-marketplace/).
The following pages list the breaking changes in IDE and plugin releases with required/recommended steps to take by plugin authors.
Changes in 2024.* (Incompatible Changes in IntelliJ Platform and Plugins API 2024.*)
Changes in 2023.* (Incompatible Changes in IntelliJ Platform and Plugins API 2023.*)
Changes in 2022.* (Incompatible Changes in IntelliJ Platform and Plugins API 2022.*)
Changes in 2021.* (Incompatible Changes in IntelliJ Platform and Plugins API 2021.*)
Changes in 2020.* (Incompatible Changes in IntelliJ Platform and Plugins API 2020.*)
Changes in 2019.* (Incompatible Changes in IntelliJ Platform and Plugins API 2019.*)
Information about bundled Third-Party Software/Libraries and their respective versions is available here (https://www.jetbrains.com/legal/third-party-software/).
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
org.apache.sanselan.util.IOUtils compatibility shim is obsolete; instead, please use JRE methods or org.apache.commons.io.IOUtils.
Use instead of com.intellij.refactoring.RefactoringHelper.prepareOperation(UsageInfo [] usages) and com.intellij.refactoring.RefactoringHelper.prepareOperation(UsageInfo [] usages, PsiElement primaryElement).
Use com.intellij.refactoring.RefactoringHelper.prepareOperation(UsageInfo [] usages, List<PsiElement> elements) instead.
Use com.intellij.refactoring.RefactoringHelper.prepareOperation(UsageInfo [] usages, List<PsiElement> elements) instead.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Use top-level method CodeFoldingConfigurableKt.applyCodeFoldingSettingsChanges instead.
Use com.intellij.ide.bookmark.providers.LineBookmarkProvider.Util instead.
The sole extension method Deferred<T>.blockingGet() contained in this package is an anti-pattern, and was not supposed to be exposed in the first place. The process mediator and the elevation service are now product modules, and no longer part of the platform.
Must be implemented.
Parameter filters: VcsLogFilterCollection was added to provide filtering capabilities to file history. Implement com.intellij.vcs.log.VcsLogFileHistoryHandler.getSupportedFilters to specify which filters are supported by this extension (currently, branch filter, revision filter and range filter are available).
Parameter filters: VcsLogFilterCollection was added to provide filtering capabilities to file history. Implement com.intellij.vcs.log.VcsLogFileHistoryHandler.getSupportedFilters to specify which filters are supported by this extension (currently, branch filter, revision filter and range filter are available).
Please provide all necessary libraries in your plugin distribution.
Use com.intellij.openapi.projectRoots.impl.ProjectJdkImpl.readExternal(Element, Function<String, SdkTypeId>) instead.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Use com.intellij.diff.tools.combined.CombinedDiffModel class instead.
Must be implemented.
Use Path.startsWith() instead.
Must be implemented.
Must be implemented.
The class is not supposed to be used directly.
Use com.intellij.ui.popup.ActionPopupStep.performActionItem(ActionItem, InputEvent) instead.
Use AnAction.getTemplatePresentation().setText() instead.
Use ActionGroup.getTemplatePresentation().setPopupGroup(boolean) instead.
Use InstalledPackagesPanel.myInstallEnabled instead.
Use com.intellij.uml.core.actions.ShowDiagramBase.findProviders(DiagramProvider<?>, BiFunction<? super DiagramProvider<?>,? super DataContext,java.lang.Object>, DataContext) instead.
Use com.intellij.codeInsight.daemon.impl.quickfix.DeleteElementFix instead.
Update code usages.
Update code usages.
Migrated to MVVM.
Migrated to MVVM.
Migrated to MVVM, hidden implementation details.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin.Companion.DELEGATE should be used instead.
org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin.Companion.DELEGATED_MEMBER should be used instead.
Use org.jetbrains.kotlin.config.JvmDefaultMode.DISABLE.
Update code usages.
Use org.jetbrains.kotlin.daemon.common.CompileService.NO_SESSION const instead.
Create a new IrDeclarationOrigin by delegation. See https://github.com/JetBrains/kotlin/blob/a3b55cf758f3a7ceb596f65507c2f61ada5266af/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrDeclarationOrigin.kt#L20.
Use com.intellij.lang.refactoring.RefactoringSupportProvider.getExtractMethodHandler instead to invoke Kotlin extract function refactoring
Use org.jetbrains.idea.maven.indices.MavenIndexImpl.getUpdateTimestamp() instead. MavenIndex is an obsolete interface now with MavenIndexImpl as the only implementation, consider using MavenGAVIndex to get information about available Maven GAV coordinates, or MavenSearchIndex to search Maven artifacts by content.
Use org.jetbrains.idea.maven.indices.MavenIndexImpl.getFailureMessage() instead. MavenIndex is an obsolete interface now with MavenIndexImpl as the only implementation, consider using MavenGAVIndex to get information about available Maven GAV coordinates, or MavenSearchIndex to search Maven artifacts by content.
Use org.jetbrains.idea.maven.indices.MavenRepositoryIndex.getRepository().getUrl() instead. Also, MavenRepositoryInfo.getKind() could be used to distinguish between local and remote repo.
Use org.jetbrains.idea.maven.indices.searcher.MavenLuceneIndexer.update(List<MavenRepositoryInfo>, Boolean) to update content for lucene indices. You should not care of GAV indices update.
Use org.jetbrains.idea.maven.indices.searcher.MavenIndicesManager.scheduleUpdateIndicesList() to update an indices list for a specific project. To get all search indices for specific project use MavenSystemIndicesManager.getClassIndexForRepository(), you can get a list of all repositories with MavenIndexUtils.getAllRepositories(Project).
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Use getDasType() instead
Use com.intellij.httpClient.actions.generation.RequestBody and com.intellij.httpClient.actions.generation.HttpRequestUrlPathInfo.Companion.create() to describe a request body that will be coomputed lazily during the corresponding request generation.
Update code usages.
Use com.intellij.openapi.fileEditor.TextEditorWithPreviewProvider instead.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Update code usages.
Private package is no longer available as an API.
Private package is no longer available as an API.
Private class is no longer available as an API.
Private package is no longer available as an API.
Override com.intellij.lang.javascript.documentation.JSDocumentationProvider.generateDoc(PsiElement, PsiElement, Ref<String>) instead
Due to RdId becoming a value class, the getId method is removed at runtime, causing unresolved method invocations. Use the method that returns long in Java and recompile the Kotlin code.
Due to RdId becoming a value class, the getId method is removed at runtime, causing unresolved method invocations. Use AbstractBuffer.writeLong(long) method in Java and recompile the Kotlin code.
Due to RdId becoming a value class, the getId method is removed at runtime, causing unresolved method invocations. Use the method that returns long in Java and recompile the Kotlin code.
Due to RdId becoming a value class, the getId method is removed at runtime, causing unresolved method invocations. Use AbstractBuffer.readLong() method in Java and recompile the Kotlin code.
Due to RdId becoming a value class, the getId method is removed at runtime, causing unresolved method invocations. Use withId(RdBindableBase, long) method in Java and recompile the Kotlin code.
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Please see updated General Threading Rules.
commons-lang2 library is going to be removed, a temporary compatibility layer (marked with @Deprecated(forRemoval = true) to highlight usages) is bundled. Please consider migrating to either commons-lang3 or commons-text libraries and bundle them with your plugin. Library commons-collections is going to be removed.
Bundle it explicitly (https://youtrack.jetbrains.com/issue/IDEA-328219) with your plugin.
This may break source-compatibility with inheritors written in Kotlin if they declare it as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare it as nullable.
Use showDumbModeWarning(Project project, AnAction action, AnActionEvent @NotNull ... events) instead.
Update code usages.
Update usages of this method. Change parameter artifactsMap value to an ArtifactMappingService instance. It can be obtained from ProjectResolverContext, or created in-place using the MapBasedArtifactMappingService.
Related mapping information is no longer accessible using this key. Artifacts mapping data is now stored in the instance of the ArtifactMappingService and can be obtained via org.jetbrains.plugins.gradle.service.project.ProjectResolverContext#getArtifactsMap().
Use com.intellij.ide.plugins.enums.PluginsGroupType.STAFF_PICKS instead.
Use com.intellij.ide.plugins.newui.SearchWords.VENDOR instead.
Use com.intellij.execution.console.ConsoleHistoryCopyHandlerKt#PROMPT_LENGTH_MARKER instead.
Action moved to a view model.
Action moved to a view model.
Moved to com.intellij.collaboration.ui.codereview.editor.EditorComponentInlaysUtilKt.controlInlaysIn.
Descriptor removed in favour of tab type com.intellij.collaboration.ui.toolwindow.ReviewTab.
Controller reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Controller reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Context reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Context reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Context reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Context reworked to viewmodel com.intellij.collaboration.ui.toolwindow.ReviewToolwindowProjectViewModel.
Added a tab viewmodel type.
Coroutine scope was added to track editor lifetime.
Incorrect EDT-reliant implementation removed.
Use com.intellij.collaboration.async.CoroutineUtilKt.disposingScope(CoroutineContext) instead.
Concrete type usage forced to ensure correct behavior
Concrete type usage forced to ensure correct behavior
Concrete type usage forced to ensure correct behavior
Removed unused return value
Allow using dialog scope in dialog panel
Handle list loading errors inside the popup
Handle list loading errors inside the popup
As a part of migration to new experimental ModCommand (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/modcommand/ModCommand.java) API, the class was removed completely. It's a part of implementation module and was never intended to be inherited by external plugins. Consider implementing LocalInspectionTool (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java) directly.
Update code usages.
Use com.intellij.lang.javascript.buildTools.npm.PackageJsonFileTemplate.create(PsiDirectory, boolean, Consumer<PsiFile>) instead.
Implement the new function to support async completion collecting.
Implement the new function to support async completion collecting.
Implement the new function to support async completion collecting.
Use new run configuration fragment builders.
Use the new run configuration fragment builders.
Use the new run configuration fragment builders.
Use the new run configuration fragment builders.
In order to not load additional code eagerly on action instantiation.
Now, the Kotlin plugin version does not include a compiler version, so the class is unnecessary. Use com.intellij.openapi.application.ApplicationInfo to get the IntelliJ version.
Now, the Kotlin plugin version does not include a compiler version, so the class is unnecessary. Use com.intellij.openapi.application.ApplicationInfo to get the IntelliJ version.
Use org.intellij.plugins.markdown.images from intellij.markdown.images module.
Use org.intellij.plugins.markdown.images.editor.ImagePsiElementFactory.createHtmlBlockWithImage instead.
Use org.intellij.plugins.markdown.images.editor.ImagePsiElementFactory.createHtmlImageTag instead.
Use org.intellij.plugins.markdown.images.editor.ImagePsiElementFactory.createImage.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
Use org.intellij.plugins.markdown.images.MarkdownImagesBundle instead.
It is now part of separate Jupyter plugin.
Should implement this method.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Use com.intellij.database.datagrid.DataGrid.setDisplayType(ModelIndex<GridColumn>, DisplayType) instead.
Use com.intellij.database.datagrid.DataGrid.getDisplayType(ModelIndex<GridColumn>) instead.
Use com.intellij.database.datagrid.DataGrid.getPureDisplayType(ModelIndex<GridColumn>) instead.
This may break source-compatibility with inheritors written in Kotlin.
To improve performance, provide either attribute for com.intellij.applicationConfigurable or com.intellij.projectConfigurable extension point (see Settings Guide).
Should implement this method.
Use com.intellij.database.dataSource.DataSourceStorage instead.
Use com.intellij.database.dataSource.DataSourceStorage instead.
Core class removed from hierarchy.
ModelIndex class is used to reference data in the table model. Row indexes in the table model start with 0, even when the table is scrolled to page N>1. Parameter type was changed to int to indicate that it is an absolute index in the DB table.
Only recompilation is needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Use resolveProject(Collection, MavenExplicitProfiles, ProgressIndicator, MavenSyncConsole, MavenConsole, MavenWorkspaceMap, boolean) instead.
Use executeGoal(Collection, String, MavenProgressIndicator, MavenConsole) instead.
Use com.intellij.velocity.java.reference.VtlPsiType to wrap Java types to return result from implementations.
It is now part of separate JSONPath plugin. Add dependency (Plugin Dependencies) on com.intellij.jsonpath to use its API.
Please bundle and use echosvg (https://github.com/css4j/echosvg) library instead.
Replaced by constant field LITERALS.
It was replaced by doUpdate(PresentationData) which should now only modify the state of its parameter.
Use getFinishedColor(JComponent c) overload instead.
Use setNameAndTooltip(PresentationData, String, String) overload instead.
Use setNameAndTooltip(PresentationData, String, String, String) overload instead.
Use setNameAndTooltip(PresentationData, String, String, SimpleTextAttributes) overload instead.
Local port is the part of the tunnel configuration, not SSH settings.
It was an accidentally exposed internal API. Please use HTMLEditorProvider.openEditor() methods, or implement your own file editor provider.
The interface is not intended to be implemented in external plugins.
The interface is not intended to be implemented in external plugins.
The inner interface was moved to upper level.
UI extracted from TypesRegistry to TypesRegistryUi. Use com.intellij.database.urlParamEditorProvider extension point to register parameter descriptor, use com.intellij.database.urlParamEditorUiProvider extension point to register parameter editor descriptor.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Only recompilation needed for classes that implement DataGrid and delegate calls to an actual DataGrid implementation.
Should implement this method.
Required to support Astro file format.
initDomains() is now awaitable to make WIP/CDP domains-dependent initialization logic possible.
ready() is now awaitable to make WIP/CDP connection-dependent initialization logic possible.
Using strings instead of paths breaks a lot of things when doing cross-platform development, and is generally not a good idea. Use com.jetbrains.gateway.ssh.HighLevelHostAccessor.makeRemotePath to prepare a path to pass into any of the methods requiring it.
This class no longer handles all paths configured in non-runtime com.jetbrains.php.config.library.PhpLibraryRootProvider. Paths configured in Include Path Settings are available with PhpIncludePathManager.getIncludePaths(). All additional paths to use as roots for resolving via PhpIncludePathManager.getAllIncludedRoots().
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Must be implemented.
Adjust your code.
Implement isApplicable() instead.
Must be implemented instead of and preferred over com.intellij.openapi.editor.EditorCopyPasteHelper.copySelectionToClipboard(Editor) which now delegates to the new method.
Use com.intellij.codeInsight.template.impl.TemplateContextTypes to get TemplateContextType extensions.
Use FoundItemDescriptor(I, int) instead.
Use com.intellij.database.dataSource.url.TypeDescriptor.ParamEditor instead.
Must be implemented.
Prefer com.intellij.database.psi.DbDataSource.getDelegateDataSource().
Avoid manually wrapping data sources.
That method was an internal method.
Do not manipulate TreePatternNodes. Use TreePatternUtils to manipulate TreePatterns.
Do not manipulate TreePatternNodes. Use TreePatternUtils to manipulate TreePatterns.
Stricter generic bound.
Construct and mutate the org.jetbrains.plugins.textmate.language.preferences.PreferencesRegistryImpl instead.
Use org.jetbrains.plugins.textmate.language.preferences.PreferencesRegistryImpl.fillFromPList(CharSequence, Plist) instead.
Use org.jetbrains.plugins.textmate.language.preferences.PreferencesRegistryImpl.clear() instead.
Instantiate org.jetbrains.plugins.textmate.language.preferences.PreferencesRegistryImpl instead.
Construct and mutate the org.jetbrains.plugins.textmate.language.preferences.ShellVariablesRegistryImpl instead.
Use org.jetbrains.plugins.textmate.language.preferences.ShellVariablesRegistryImpl.fillVariablesFromPlist(CharSequence, Plist) instead.
Use org.jetbrains.plugins.textmate.language.preferences.ShellVariablesRegistryImpl.clear() instead.
Instantiate org.jetbrains.plugins.textmate.language.preferences.ShellVariablesRegistryImpl instead.
Construct and mutate the org.jetbrains.plugins.textmate.language.preferences.SnippetsRegistryImpl instead.
Use org.jetbrains.plugins.textmate.language.preferences.SnippetsRegistryImpl.register(TextMateSnippet) instead.
Use org.jetbrains.plugins.textmate.language.preferences.SnippetsRegistryImpl.clear() instead.
Instantiate org.jetbrains.plugins.textmate.language.preferences.SnippetsRegistryImpl() instead.
Use com.intellij.httpClient.http.request.HttpRequestVariableSubstitutorImpl.create(Project, HttpRequestEnvironment) for constructing instances.
Use com.intellij.webpack.WebpackConfigManager instead.
Use com.intellij.webpack.WebpackConfigManager.Companion instead.
Use com.intellij.microservices.url.inlay.UrlPathInlayHint.getContext to obtain corresponding UrlPathContext instance.
Use com.jetbrains.gateway.ssh.SshMultistagePanelContext.getConfig() instead.
Use com.jetbrains.gateway.ssh.SshMultistagePanelContext.setConfig(SshConfig) instead.
Use org.jetbrains.yaml.YAMLElementTypes fields directly.
Top level packages of Terraform org.intellij.plugins.hcl and org.intellij.plugins.hil moved to single org.intellij.terraform.
Use com.intellij.openapi.actionSystem.DataProvider class instead.
Removed along with com.intellij.openapi.actionSystem.TypeSafeDataProvider.
Use com.intellij.openapi.vcs.changes.ui.ChangesBrowserBase or com.intellij.openapi.vcs.changes.ui.SimpleChangesBrowser instead.
Removed along with com.intellij.openapi.vcs.changes.ui.ChangesBrowser.
Use com.intellij.openapi.vcs.changes.ui.ChangesTree or com.intellij.openapi.vcs.changes.ui.ChangesTreeImpl instead.
Method does not make sense anymore, please see JBR-4328 (https://youtrack.jetbrains.com/issue/JBR-4328).
Use com.intellij.vcs.log.VcsLogHighlighter.getStyle(int, VcsShortCommitDetails, int, boolean) instead.
Use com.intellij.DynamicBundle.getResourceBundle(java.lang.ClassLoader, java.lang.String) instead
Use com.intellij.codeInspection.javaDoc.JavadocDeclarationInspection.ADDITIONAL_TAGS field instead of JavaDocLocalInspection.myAdditionalJavadocTags.
Use org.jetbrains.idea.devkit.DevKitIcons instead.
Use org.jetbrains.idea.devkit.DevKitIcons.Gutter instead.
Method hidden for better encapsulation.
Factory is now a factory.
Better API introduced in the form of com.intellij.collaboration.auth.ui.AccountsDetailsLoader.
Better API introduced in the form of com.intellij.collaboration.auth.ui.AccountsDetailsLoader.
Icon provider implementation changes to async.
Hidden implementation details.
Hidden implementation details.
Hidden implementation details.
To shorten Java callers.
Please update usages.
Should be used only in tests (marked with @TestOnly).
Use com.intellij.lang.javascript.buildTools.bundler.WebBundlerConfig instead.
Use com.intellij.lang.javascript.buildTools.bundler.WebBundlerResolve instead.
A regular String class is used instead.
log4j library removed from IntelliJ Platform, please see this blog post (https://blog.jetbrains.com/platform/2022/02/removing-log4j-from-the-intellij-platform/) for migration instructions.
log4j library removed from IntelliJ Platform, please see this blog post (https://blog.jetbrains.com/platform/2022/02/removing-log4j-from-the-intellij-platform/) for migration instructions.
log4j library removed from IntelliJ Platform, please see this blog post (https://blog.jetbrains.com/platform/2022/02/removing-log4j-from-the-intellij-platform/) for migration instructions.
Implement it instead of removed one.
JavaFullClassNameIndex now takes CharSequence instead of its hashCode to allow specific optimizations.
JavaFullClassNameIndex now takes CharSequence instead of its hashCode to allow specific optimizations.
JavaFullClassNameIndex now takes CharSequence instead of its hashCode to allow specific optimizations.
JSch library (https://mvnrepository.com/artifact/com.jcraft/jsch) was removed, bundle it with your plugin instead.
DialogAppender now implements java.util.logging.Handler, use setFilter method if you need to apply a filter.
Todo index is removed from the indexing procedure, use specific calcIdEntries or calcTodoEntries method instead.
Todo index is removed from the indexing procedure, use specific calcIdEntries or calcTodoEntries method instead.
Method moved to com.intellij.util.ui.StyleSheetUtil
Support for default accounts required a signature change.
Some methods from a companion object were moved to CommonMarkdownConstraints and to extension functions on MarkdownConstraints.
Some methods from a companion object were moved to CommonMarkdownConstraints and to extension functions on MarkdownConstraints.
Use CommonMarkdownConstraints for default method implementations instead.
AtxHeaderProvider now always requires at least one space between # and its content as specified by the CommonMark spec.
Use org.intellij.markdown.html.HtmlGenerator.generateHtml(org.intellij.markdown.html.HtmlGenerator.TagRenderer) instead.
Use org.intellij.markdown.parser.markerblocks.MarkerBlockProvider.Companion.passSmallIndent(CharSequence, Integer) instead.
GridDataRequest is a part of new API for async loading of table data. It's not possible to keep old method with default implementation because DataProducer will no longer have dependency on DataRequest. Plugins need to be recompiled to maintain bytecode compatibility.
The signature of the method was changed in the interface com.intellij.database.datagrid.DataConsumer that is now a part of new API for async loading of table data. Change the parameter type of the overridden method and recompile plugin to maintain bytecode compatibility.
The signature of the method was changed in the interface com.intellij.database.datagrid.DataConsumer that is now a part of new API for async loading of table data. Change the parameter type of the overridden method and recompile plugin to maintain bytecode compatibility.
The signature of the method was changed in the interface com.intellij.database.datagrid.DataConsumer that is now a part of new API for async loading of table data. Change the parameter type of the overridden method and recompile plugin to maintain bytecode compatibility.
Method was removed because we refactor table editor API. It will not depend on Dbms anymore. Please use ObjectFormatter.objectToString instead.
DatabaseDataKeys no longer extends DatabaseDataKeysCore because DatabaseDataKeys was moved to a separate module for table editor. DATA_SOURCE_KEY now has to be accessed directly via DatabaseDataKeysCore.
Method was removed because we refactor table editor API. New API will allow to use table editor in other products and fully customize it.
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
Set system property idea.force.use.core.classloader to true.
Please use workarounds (https://youtrack.jetbrains.com/issue/IDEA-278926#focus=Comments-27-5561012.0-0).
Use com.intellij.ui.mac.MacMessages.showMessageDialog(String, String, String[], boolean, Window, int, int, DoNotAskOption) instead.
Use com.intellij.openapi.ui.MessageDialogBuilder.doNotAsk(DoNotAskOption) instead.
Use com.intellij.ide.util.projectWizard.WizardContext.getUserData(AbstractWizard.KEY) instead.
Use com.intellij.openapi.ui.TextComponentAccessors.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT instead.
Use com.intellij.execution.process.ColoredOutputTypeRegistryImpl.getAnsiColorKey(int) instead.
Recompile the dependant code or use com.intellij.diagnostic.PerformanceWatcherImpl.SnapshotImpl instead.
This parameter was never needed, but led to code coupling.
This parameter was never needed, but led to code coupling.
This parameter was never needed, but led to code coupling.
Added Language parameter.
Provides short description.
Provides description text for given case.
Provides preview text for given case.
Please safe-cast and use com.intellij.openapi.wm.ex.ToolWindowEx.getEmptyText().
Please use createCustomComponent(Presentation, String), one shall not depend on dataContext there.
Please use addSettingsProvider(EditorSettingsProvider) to configure Editor as editor creation may be postponed now.
Removed unnecessary inheritance.
Method was dropped to avoid supporting outdated behavior.
Method was dropped to avoid supporting outdated behavior.
There is no need to pass a Restart action as a constructor parameter, it should be created inside the com.jetbrains.python.console.PydevConsoleRunnerImpl#createRerunAction method
To support dynamic plugins.
To support dynamic plugins.
Expression Language (EL) was extracted from com.intellij.jsp ("Java Server Pages (JSP)") plugin to new com.intellij.javaee.el (Java EE: Expression Language (EL)) plugin. com.intellij.jsp has mandatory dependency on com.intellij.javaee.el. 10+ Ultimate plugins (Spring, Java EE, Frameworks) now have optional dependency on com.intellij.javaee.el plugin (mandatory dependency to com.intellij.jsp was removed).
To support dynamic plugins.
Use new class from com.intellij.javaee.el plugin instead or com.intellij.jsp.el.impl.JspELResolveUtil from com.intellij.jsp plugin.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
Use new class from com.intellij.javaee.el plugin instead.
To support dynamic plugins.
Various package renames to support dynamic plugins
Old package name | New package name |
|---|---|
com.intellij.javaee.serverInstances | com.intellij.javaee.appServers.serverInstances |
com.intellij.javaee.appServerIntegrations | com.intellij.javaee.appServers.appServerIntegrations |
com.intellij.javaee.deployment | com.intellij.javaee.appServers.deployment |
com.intellij.javaee.run | com.intellij.javaee.appServers.run |
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
To support dynamic plugins.
Internal class not to be used by 3rd party.
Use persistent range markers instead, see com.intellij.openapi.editor.Document.createRangeMarker(int, int, boolean) with surviveOnExternalChange=true.
Use persistent range markers instead, see com.intellij.openapi.editor.Document.createRangeMarker(int, int, boolean) with surviveOnExternalChange=true.
Use com.intellij.openapi.fileTypes.PlainTextFileType instead.
Use com.intellij.openapi.updateSettings.impl.PlatformUpdates instead.
Use com.intellij.openapi.updateSettings.impl.PlatformUpdates instead of com.intellij.openapi.updateSettings.impl.CheckForUpdateResult.
Use com.intellij.ide.plugins.advertiser.PluginData instead.
Use either com.intellij.ide.plugins.PluginManagerCore.disablePlugin(PluginId) or com.intellij.ide.plugins.PluginEnabler.disablePlugins(Collection) instead.
com.intellij.ide.plugins.PluginManagerMain.PluginEnabler has been renamed to com.intellij.ide.plugins.PluginEnabler.
Use com.intellij.ssh.ui.unified.SshConfigVisibility instead.
SshConfigConfigurable.Visibility has been renamed to SshConfigVisibility.
SshConfigConfigurable.Visibility has been renamed to SshConfigVisibility.
Use org.jetbrains.uast.UAnnotated.getUAnnotations() instead.
Previously this circularly referenced org.jetbrains.uast.UAnnotated.getAnnotations(), which has been removed.
Use isMainMenuOrActionSearch(String) method instead.
Implementations should return non-null VirtualFile instance.
Use java.util.List instead of java.util.Map.
Use com.intellij.database.dataSource.DatabaseCredentialsAuthProviderUi.UserWidget instead.
Use com.intellij.database.actions.DbDeleteProvider.getDeleteProvider() instead.
Use com.intellij.database.model.RawConnectionConfig.getUrl() instead. Or use com.intellij.database.dataSource.DatabaseConnectionEstablisher.processInterceptors().
Use org.jetbrains.plugins.github.authentication.GithubAuthenticationManager instead.
Use org.jetbrains.plugins.github.authentication.GithubAuthenticationManager instead.
Use org.jetbrains.plugins.github.authentication.GithubAuthenticationManager instead.
Use org.jetbrains.plugins.github.authentication.GithubAuthenticationManager instead.
Use org.jetbrains.plugins.github.authentication.GithubAuthenticationManager instead.
Use com.intellij.spring.SpringApiIcons instead.
Use com.intellij.spring.SpringApiIcons.Gutter instead.
As the result of the refactoring aimed at fixing PY-48799 (https://youtrack.jetbrains.com/issue/PY-48799), for dict literals containing only string keys we infer PyTypedDictType now, so there's no need to match dict literals with TypedDicts. There's a new method for comparing the inferred TypedDicts with the given ones: com.jetbrains.python.psi.types.PyTypedDictType.Companion.match(PyType, PyTypedDictType, TypeEvalContext).
The storage checks for corruption automatically, there is no need of any explicit additional checks.
Add a dependency on the com.intellij.jsp plugin and replace the reference with com.intellij.lang.jspx.JspxLanguageImpl.INSTANCE.
Add a dependency on the com.intellij.jsp plugin and replace the reference with com.intellij.lang.jsp.NewJspLanguage.getInstance().
New API is more abstract which allows to review all already found items before making "deduplication" decision. Also consider implementing com.intellij.ide.actions.searcheverywhere.AbstractEqualityProvider instead of com.intellij.ide.actions.searcheverywhere.SEResultsEqualityProvider.
Enum class SEEqualElementsActionType was converted to sealed class with the same name.
Enum class SEEqualElementsActionType was converted to sealed class with the same name.
Enum class SEEqualElementsActionType was converted to sealed class with the same name.
Please use updated sqlite-jdbc API.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
Project is now only accessible via com.intellij.openapi.ui.playback.PlaybackContext.getProject() since it may change during script execution.
Override new method JsonWidgetSuppressor.isCandidateForSuppress(VirtualFile, Project) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/json/src/com/jetbrains/jsonSchema/extension/JsonWidgetSuppressor.java) for quick check in EDT before suppressSwitcherWidget() is called on background thread.
Replaced by com.intellij.httpClient.execution in HTTP Client plugin.
Replaced by com.intellij.httpClient.actions in HTTP Client plugin.
Replaced by com.intellij.httpClient.converters in HTTP Client plugin.
Replaced by com.intellij.httpClient.http.request in HTTP Client plugin.
Rename of packages to .java. specific variants
Old package name | New package name |
|---|---|
com.intellij.uml.utils | com.intellij.uml.java.utils |
com.intellij.uml.project | com.intellij.uml.java.project |
com.intellij.uml.jigsaw | com.intellij.uml.java.jigsaw |
Visibility has been renamed to DescriptorVisibility.
Recompile the dependant code.
To support dynamic plugins.
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
IDE and Java VersionsJava 21 is required when targeting 2024.2 and later only.
Java 17 is required (blog post (https://blog.jetbrains.com/platform/2022/08/intellij-project-migrates-to-java-17/)) when targeting 2022.2 and later only.
Java 11 is required (blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/)) when targeting 2020.3 and later only.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
Constructors of FileContentImpl were replaced with factory methods, use FileContentImpl#createByContent(VirtualFile, byte[]).
Replaced with ChangeTo(String, PsiElement, TextRange).
Replaced with SpellcheckingStrategy.getDefaultRegularFixes(boolean, String, PsiElement, TextRange).
This may break source-compatibility with inheritors written in Kotlin.
The Swing Inspector functionality has been removed from the product.
The Swing Inspector functionality has been removed from the product.
The Swing Inspector functionality has been removed from the product.
The Swing Inspector functionality has been removed from the product.
Now the children are flattened: brackets for all the dimensions are direct children of the PsiTypeElement that represent the multi-dimensional array. This change doesn't break source or binary compatibility but may produce behavioral changes in the code that traverses the tree of Java source files.
This change supports identifying whether a type annotation is attached to an inner class or a particular dimension of a multi-dimensional array. This change doesn't break source or binary compatibility but may produce behavioral changes for callers.
See Breaking Changes in PhpStorm 2020.3 (Incompatible PHP OpenAPI changes in PhpStorm 2020.3).
This may break source-compatibility with inheritors written in Kotlin.
It is no longer used in parsing.
It is no longer used in parsing.
Use com.jetbrains.python.psi.FutureFeature instead.
It was not processed carefully, it should be enough to pass editable SDK instead.
Use python.sdk.field.is.empty from messages.PySdkBundle instead.
Use python.venv.base.label from messages.PySdkBundle instead.
Use python.interpreter.label from messages.PySdkBundle instead.
It is true for all supported python versions.
When targeting 2020.3, please see this migration guide (https://blog.jetbrains.com/clion/2020/12/migration-guide-for-plugins-2020-3/).
Plugins should migrate to JCEF (Embedded Browser (JCEF)). Alternatively, add an explicit dependency on JavaFX Runtime for Plugins (https://plugins.jetbrains.com/plugin/14250-javafx-runtime-for-plugins).
This may break source-compatibility with clients that pass a more specific processor. Passing a more specific processor was illegal before because the processElements passes every descendant PsiElement to the processor regardless of its type. However, this worked with some poorly written clients, e.g. PsiElementProcessor.CollectFilteredElements and PsiElementProcessor.FindFilteredElement (both deprecated now). To simplify the migration, a new three-arg processElements(element, elementClass, processor) is introduced that filters by element class. In most cases, the simplest migration would be to add a wanted element class as a second argument. However, it's advised to use SyntaxTraverser API instead, which is more rich and flexible.
This was an internal utility method not intended for use in plugins. Use FileTypeManager.getInstance().findFileTypeByName() instead.
Javassist (https://github.com/jboss-javassist/javassist) library was removed, bundle it with your plugin instead.
This field leaked instances of plugin's extensions on plugin unloading. Use com.intellij.compiler.backwardRefs.LanguageCompilerRefAdapter#EP_NAME.getExtensionList() directly instead.
Add org.codehaus.groovy:groovy-ant dependency.
Add org.codehaus.groovy:groovy-test dependency.
Add org.codehaus.groovy:groovy-test dependency.
Use classes from org.apache.groovy.json.internal package.
Update inheritors accordingly.
Update call sites accordingly.
Use it instead of MarkupModel.addLineHighlighter(int, int, TextAttributes).
Use it instead of MarkupModel.addRangeHighlighter(int, int, int, TextAttributes, HighlighterTargetArea).
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
Please remove the plugin code supporting Java 13 language level features. IntelliJ IDEA supports preview features of the latest Java release and one upcoming release (if available).
Use com.intellij.diff.DiffVcsDataKeys.REVISION_INFO instead.
Use com.intellij.codeInsight.actions.VcsFacadeImpl.getVcsInstance().getChangedElements(...) instead.
Required for more tight control of task scheduling. Use org.jetbrains.plugins.github.util.LazyCancellableBackgroundProcessValue.Companion#create(ProgressManager, (ProgressIndicator) -> T) instead of subclassing.
Required for more tight control of task scheduling. Use org.jetbrains.plugins.github.util.LazyCancellableBackgroundProcessValue.Companion#create(ProgressManager, (ProgressIndicator) -> T) instead of subclassing.
Model was made disposable and it is now required to pass parent disposable in constructor.
Use org.jetbrains.plugins.github.util.GHProjectRepositoriesManager.getKnownRepositories() instead.
Please adjust/recompile the code.
Plugin com.intellij.javaee Java EE: EJB, JPA, Servlets has been split to:
com.inteellij.javaee Java EE Platform - main plugin other JavaEE/Jakarta plugins depend on
com.intellij.javaee.app.servers.integration Java EE: Application Servers Integration
com.intellij.javaee.ejb Java EE: Enterprise Java Beans (EJB)
com.intellij.javaee.jpa Java EE: JPA
com.intellij.javaee.web Java EE: Web/Servlets
JSLint functionality has been unbundled and moved to a separate plugin. Issue (https://youtrack.jetbrains.com/issue/WEB-44511)
See Breaking Changes in PhpStorm 2020.2 (Incompatible PHP OpenAPI changes in PhpStorm 2020.2).
This change was required to implement import layout order for Kotlin. KotlinPackageEntryTable can be used in the same manner as PackageEntryTable.
PythonDialectsTokenSetProvider became an application service, use PythonDialectsTokenSetProvider.getInstance() instead.
Use PythonLanguageLevelPusher.getLanguageLevelForVirtualFile(Project, VirtualFile) instead.
'Generate Ant build' functionality is removed from the IDE. Delete the code extending this or replace it with a dependency on the generate-ant plugin.
Implement it in ModuleLevelBuilder's implementation.
Use com.intellij.codeInsight.TargetElementUtil instead.
Use com.intellij.psi.stubs.PlatformPrebuiltStubsProviderBase instead.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
This may break source-compatibility with inheritors written in Kotlin if they declare parameter type as nullable.
Use com.intellij.testFramework.ReadOnlyLightVirtualFile-based PSI instead.
In rare cases existing Kotlin code might become uncompilable due to some problems in the Kotlin compiler: if a method is used or overridden, and is written in Java, and returns an array annotated as @Nullable or @NotNull.
This may break source-compatibility with inheritors written in Kotlin.
This may break source-compatibility with inheritors written in Kotlin.
The dependency must be declared (Plugin Dependencies) explicitly now by the <depends> ("depends" in "Plugin Configuration File") element:
Add <depends>com.intellij.platform.images</depends> in plugin.xml (Plugin Configuration File)
Add to build.gradle:
intellij {
plugins = ['platform-images']
}If your plugin depends on other plugins using com.intellij.platform.images (e.g., CSS), please make sure to use gradle-intellij-plugin >=0.4.19
Use com.jetbrains.python.psi.types.PyCallableType instead.
Use com.jetbrains.python.psi.types.PyCallableType instead of com.jetbrains.python.psi.PyCallExpression.PyMarkedCallee.
Please see Verifying Plugin Compatibility on how to use Plugin Verifier and IDE inspections to check such problems.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Non-listed changesChanges from API marked with @Deprecated(forRemoval=true) or any of ApiStatus (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) @Experimental, @ScheduledForRemoval, or @Internal are not listed here, as incompatible changes are to be expected.
For API annotated with ApiStatus.@Internal/@IntellijInternalApi, see Internal API Migration for more details and replacements.
Use com.intellij.psi.codeStyle.CommonCodeStyleSettings.getLocalCodeStyleSettings(Editor, int) instead.
Use com.intellij.codeInsight.lookup.EqTailType.INSTANCE instead.
Use com.intellij.codeInsight.lookup.CommaTailType.INSTANCE instead.
Use com.intellij.codeInsight.completion.CompletionUtil.getActionShortcut(String) instead
Use com.intellij.codeInsight.AutoPopupControllerImpl.runTransactionWithEverythingCommitted(Project, Runnable) instead
Use com.intellij.codeInsight.completion.util.CompletionStyleUtil.getCodeStyleSettings(InsertionContext) instead
Use com.intellij.codeInsight.completion.PrefixMatcher.sortMatching(Collection) instead
Use com.intellij.fileType extension point instead.
Use com.intellij.util.containers.ContainerUtil#newConcurrentSet instead.
Use com.intellij.openapi.editor.EditorFactory.getInstance() instead.
Use org.apache.http.conn.ssl.DefaultHostnameVerifier instead.
See BuildNumber.asString, BuildNumber.getBaselineVersion() and BuildNumber.getComponents() as alternatives.
Use DeploymentConfigurationManager.createAndRunConfiguration(ServerType, RemoteServer, DeploymentSourceType) instead.
Use com.intellij.openapi.vcs.changes.ui.ChangesListView.UNVERSIONED_FILE_PATHS_DATA_KEY instead.
Use com.intellij.openapi.vcs.VcsVFSListener.myProcessor.acquireAddedFiles() instead.
Use com.intellij.openapi.vcs.VcsVFSListener.myProcessor.acquireAllDeletedFiles().deletedFiles instead.
Use com.intellij.openapi.vcs.VcsVFSListener.myProcessor.acquireAllDeletedFiles().deletedWithoutConfirmFiles instead.
Use com.intellij.openapi.vcs.VcsVFSListener.myProcessor.acquireExceptions() or com.intellij.openapi.vcs.VcsVFSListener.myProcessor.addException(VcsException exception) instead.
Use com.intellij.testFramework.ServiceContainerUtil#registerExtension(BaseExtensionPointName, T, Disposable) instead.
Use com.intellij.testFramework.ServiceContainerUtil#registerExtension(BaseExtensionPointName, T, Disposable) instead.
Use com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor.getModuleTypeId() instead (see com.intellij.openapi.module.ModuleTypeId).
Use new type instead.
Use constructor GroovyScriptTypeDetector(GroovyScriptType) instead, and com.intellij.fileType to register additional extensions.
Use org.jetbrains.plugins.cucumber.javascript.CucumberJavaScriptStepDefinitionCreator#createStepDefinition(GherkinStep, PsiFile, boolean) instead.
Use org.jetbrains.plugins.cucumber.psi.GherkinStep#getName() instead.
See org.jetbrains.plugins.cucumber.steps.reference.CucumberStepReference#multiResolveInner() instead.
Java specific method was moved to CucumberJava implementation.
Do not use SQL dialect classes directly.
Do not use SQL dialect classes directly.
Do not use SQL dialect classes directly.
This was done as part of RUBY-24760 (https://youtrack.jetbrains.com/issue/RUBY-24760) in order to move to new Context-less approach.
com.jetbrains.python.inspections.PythonVisitorFilter class moved to package com.jetbrains.python.psi
Use com.jetbrains.python.refactoring.PyRefactoringUtil.addElementToStatementList(PsiElement, PyStatementList, boolean) instead
Use com.jetbrains.python.psi.search.PySearchUtilBase.excludeSdkTestsScope(Project) instead.
Use icons.PythonPsiApiIcons.PropertySetter instead.
Use icons.PythonPsiApiIcons.PropertyGetter instead.
Use com.jetbrains.python.PyPsiBundle instead.
Use com.jetbrains.python.PyPsiBundle instead.
Obtain reference to extension points via (Project)ExtensionPointName.findExtension() in your constructor instead.
Use com.intellij.openapi.components.BaseState.map() instead.
fast-serialization (https://github.com/RuedigerMoeller/fast-serialization) library was removed, please use com.intellij.serialization.ObjectSerializer instead.
Java-WebSocket (https://github.com/TooTallNate/Java-WebSocket) library was removed, bundle it with your plugin instead.
The signature of this function has been seriously changed without the possibility to keep the old function. Change invocations and overriding of that function according to new parameters and recompile the code.
This method has been pulled up to the base class Cell; since it has default parameters, it's a binary breaking change in Kotlin. Recompile your code to pick up the new signature.
YourKit library has been extracted into the separate plugin, which is not bundled in all IDEs by default. YourKit library is a library for profiling IDE, and its util classes shouldn't be used for general purposes. Instead of com.yourkit.util.Strings please use org.apache.commons.lang.StringUtils. Instead of com.yourkit.util.ArrayUtil please use org.apache.commons.lang.ArrayUtils.
Please bundle performanceTesting plugin (https://plugins.jetbrains.com/plugin/7819-performance-testing) in case you would like to bundle YourKit profiler within your IDE.
Please bundle performanceTesting plugin (https://plugins.jetbrains.com/plugin/7819-performance-testing) in case you would like to bundle YourKit profiler within your IDE.
Please use com.intellij.psi.impl.PsiElementBase or one of its descendants as a base class for PSI elements, e.g. com.intellij.extapi.psi.ASTWrapperPsiElement, as suggested in Custom Language Support Tutorial (3. Grammar and Parser).
Please use different base class for PSI elements.
Please use the com.intellij.editorActionHandler extension point to register a different handler for the action.
Please use the com.intellij.editorActionHandler extension point to register a different handler for the action.
Please use com.intellij.lexer.RestartableLexer.isRestartableState(int state) instead.
Implement method in RestartableLexer implementations.
Implement method in RestartableLexer implementations.
Bundled Kotlin library is updated to 1.3, so the plugins must migrate (https://blog.jetbrains.com/kotlin/2018/09/kotlin-1-3-rc-is-here-migrate-your-coroutines/) to the stable versions of coroutines.
Use com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl.<init>(Project, FileStatusManager, FileIndexFacade, ProjectManager, DefaultVcsRootPolicy).
Implement the method in DocumentWindow implementations.
Implement actionPerformedAfterChecks instead of actionPerformed.
Implement actionPerformedAfterChecks instead of actionPerformed.
Use com.intellij.util.loader.NativeLibraryLoader.loadPlatformLibrary instead.
Remove custom implementation.
In most of the cases, it's enough to recompile the code. It may also be needed to check that the code doesn't rely on the field's type.
Field type has been generalized. In most of the cases, it's enough to recompile the code of the plugin.
Field type has been generalized. In most of the cases, it's enough to recompile the code of the plugin.
Field type has been generalized. In most of the cases, it's enough to recompile the code of the plugin.
Field type has been generalized. In most of the cases, it's enough to recompile the code of the plugin.
Field type has been generalized. In most of the cases, it's enough to recompile the code of the plugin.
The following pages list notable changes and new features in IDE releases.
Plugin authors are encouraged to verify their compatible releases and take advantage of the latest API additions.
Staying up to dateSubscribe to Marketplace Developer News (https://jb.gg/mp-updates) to receive news and announcements. Also follow JBPlatform (https://x.com/JBPlatform/) on X (formerly Twitter) and visit JetBrains Platform Blog (https://blog.jetbrains.com/platform/) and JetBrains Marketplace on LinkedIn (https://www.linkedin.com/showcase/jetbrains-marketplace/).
Changes in 2024.* (Notable Changes in IntelliJ Platform and Plugins API 2024.*)
Changes in 2023.* (Notable Changes in IntelliJ Platform and Plugins API 2023.*)
Changes in 2022.* (Notable Changes in IntelliJ Platform and Plugins API 2022.*)
Changes in 2021.* (Notable Changes in IntelliJ Platform and Plugins API 2021.*)
Changes in 2020.* (Notable Changes in IntelliJ Platform and Plugins API 2020.*)
Changes in 2019.* (Notable Changes in IntelliJ Platform and Plugins API 2019.*)
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Initial plugin localization ("Translated Elements" in "Providing Translations") capabilities.
It is now recommended to use Kotlin Coroutines for asynchronous code.
Highlighting is now performed more efficiently, please refer to "Order of Running Highlighting" in "Syntax and Error Highlighting".
Language plugins using LSP can now provide their status for "Status Bar Integration" in "Language Server Protocol (LSP)".
Note change for "Using ProjectRootManager as Dependency" in "PSI Performance".
Saving project/application settings (Settings) is no longer performed on EDT (General Threading Rules) to avoid freezes. Issue (https://youtrack.jetbrains.com/issue/IJPL-127/Save-project-application-settings-on-background-thread)
Several plugins (Cucumber Groovy, Cucumber Java) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Please see updated General Threading Rules.
"External Annotator" in "Syntax and Error Highlighting" can now run in during indexing.
Custom language plugins with many inspections should consider registering a default visitor to improve processing, see "Inspections Performance" in "Code Inspections and Intentions".
Several plugins (Android, Ant, GlassFish, Plugin DevKit) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Provide custom language support by using language servers, see Language Server Protocol (LSP).
Use dedicated API ("How do I check the presence of a JVM library?" in "PSI Cookbook") to check presence via class FQN or Maven coordinates.
Embedded code is shown with syntax highlighting ("Code Snippets" in "Code Inspections").
Intentions not modifying code can specify <skipBeforeAfter>true</skipBeforeAfter> in their registration.
SVG pictures are now shown using JCEF (Embedded Browser (JCEF)) instead of rendering via the Apache Batik library (Details (https://youtrack.jetbrains.com/issue/IDEA-230850)).
Several plugins (Play 1, Resin, Struts 2, tcServer) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Documentation for custom languages is provided through the DocumentationTarget (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-impl/src/com/intellij/platform/backend/documentation/DocumentationTarget.kt) API for versions 2023.1 or later.
Code inspections (Code Inspections) can provide additional options in a declarative ("Declarative Inspection Options" in "Inspection Options") way which has several benefits over the UI-based ("UI-Based Inspection Options" in "Inspection Options") approach.
Accessing index data in nested calls ("Nested Index Access" in "File-Based Indexes") is now possible. NOTE: Please do not use yet This is known to cause problems under certain conditions, please watch this issue (https://youtrack.jetbrains.com/issue/IJPL-265/Nested-index-lookups-still-leads-to-deadlocks).
FileTypeIndex.IndexChangeListener (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/indexing-api/src/com/intellij/psi/search/FileTypeIndex.java) allows monitoring the addition/removal of files by FileType.
Annotators ("Annotator" in "Syntax and Error Highlighting") can implement DumbAware to run during indexing (e.g., providing additional syntax highlighting).
Newly introduced ApiStatus.@Obsolete marks API that should not be used for new code, see "Obsolete API" in "Verifying Plugin Compatibility".
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
Intentions and Quick Fix actions now display a preview for the code changes. Read this article (Intention Action Preview) to learn more about this feature.
Specify <language> in com.intellij.intentionAction EP registration to avoid instantiating language-specific intentions in non-relevant places.
Alternative extension point to implement "Brace Matching" in "Additional Minor Features" in background thread.
Delegation of formatting can be controlled using dedicated extension point ("Formatting" in "Language Injection").
Implementations of AnAction need to override getActionUpdateThread() as detailed in "Principal Implementation Overrides" in "Actions".
All rules for threading (General Threading Rules) are now checked in tests as well.
See "New UI Icons" in "Working with Icons" on how to provide additional icons.
Several plugins (Haml, Jakarta EE: WebSockets, JBoss Seam, Spring WebSocket, Stylus, Tapestry, Vaadin, ZKM-Unscramble) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Highlight references automatically via "Additional Highlighting" in "References and Resolve".
The New Project wizard has been refreshed and some base ModuleBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java) classes return false from the isAvailable() method. If your module builder extends a base class and is hidden in the 2022.1 wizard, override the method to return true.
Available as com.jetbrains.intellij.platform:external-system-test-framework from IntelliJ Platform Artifacts Repositories, see "Testing" in "External System Integration".
Several plugins (Grails, Jakarta EE: Batch Applications, Jakarta EE: Server Faces (JSF), Jakarta EE: Web Services (JAX-WS), Jetty, Smali Support, Spring Batch, Spring Integration Patterns, Spring Web Services, WebLogic, WebSphere) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Available as com.jetbrains.intellij.maven:maven-test-framework from IntelliJ Platform Artifacts Repositories.
Early Access Program (EAP) releases of upcoming versions are available here (https://eap.jetbrains.com).
Allows integration of standalone tools like shfmt: "External Code Formatter" in "Code Formatter".
Use new method CustomComponentAction.updateCustomComponent(Component, Presentation) (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/ex/CustomComponentAction.java) to synchronize given Presentation and component state.
Use getPriorityRange() in AnnotationSession (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/lang/annotation/AnnotationSession.java)/LocalInspectionToolSession (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionToolSession.java) to optimize highlighting for coarse-grained files (e.g., PsiPlainTextFile files with single node).
Now com.intellij.ui.IconManager.createDeferredIcon() doesn't use iconProducer which might result in "wrong" composite icons and failed assertions. Override UsefulTestCase.isIconRequired() returning true to restore production icons. Alternatively, invoke Registry.get("psi.deferIconLoading").setValue(false) in setUp() and Registry.get("psi.deferIconLoading").resetToDefault() in tearDown().
Please obtain necessary components only when needed (logged as ERROR now).
Use LanguageInjectionContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/injection/general/LanguageInjectionContributor.java) (com.intellij.languageInjectionContributor EP) and LanguageInjectionPerformer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/lang/injection/general/LanguageInjectionPerformer.java) (com.intellij.languageInjectionPerformer EP) to customize language injection ("LanguageInjectionContributor and LanguageInjectionPerformer" in "Language Injection").
Several plugins (Arquillian, AspectJ, CoffeeScript, Debugger Support for JSP (JSR45), Drools, Guice, Helidon, Java EE: Enterprise Java Beans (EJB), Spring OSGi, Spring Web Flow) have been unbundled. The IDE will suggest installation if the project contains related framework dependency. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Override ReferenceImporter#isAddUnambiguousImportsOnTheFlyEnabled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-impl/src/com/intellij/codeInsight/daemon/ReferenceImporter.java) and provide corresponding user setting. Implement HintAction with fixSilently() and hook it up to highlighting as a quick fix for unresolved reference.
Implement FileEncodingProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/vfs/encoding/FileEncodingProvider.java) and register in com.intellij.fileEncodingProvider extension point.
JBCefOsrHandlerBrowser (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/jcef/JBCefOsrHandlerBrowser.java) forwards to custom CefRenderHandler, e.g., for off-screen rendering.
Use LightJavaCodeInsightFixtureTestCase4 (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/java/testFramework/src/com/intellij/testFramework/fixtures/LightJavaCodeInsightFixtureTestCase4.kt).
We've published our roadmap for the IntelliJ Platform for 2020: Part I (https://blog.jetbrains.com/idea/2019/12/intellij-platform-roadmap-for-2020/) Part II (https://blog.jetbrains.com/idea/2020/01/intellij-based-ide-features-roadmap-for-2020/)
Using Java 11 is now required, please see blog post (https://blog.jetbrains.com/platform/2020/09/intellij-project-migrates-to-java-11/).
Internal changes related to a significant redesign of the representation of project models have been made, please see blog post (https://blog.jetbrains.com/platform/2020/10/new-implementation-of-project-model-interfaces-in-2020-3/) for details. This shouldn't affect any plugins using the IntelliJ API properly and which don't access internal classes.
For elements whose IElementType implements this interface, platform attempts reparse when a modification is made right before or after the leaf element preventing reparsing the whole file.
Use HtmlBuilder (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/text/HtmlBuilder.java) for generating formatted content, e.g., for Documentation (Documentation).
Implement HtmlEmbeddedContentSupport (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/xml/xml-psi-impl/src/com/intellij/html/embedding/HtmlEmbeddedContentSupport.kt) and register in com.intellij.html.embeddedContentSupport extension point to embed arbitrary tokens into any tag or attribute. Please note that old API from com.intellij.lexer.BaseHtmlLexer is no longer working.
New features in Action System (Actions): <override-text> works now for <group> ("group" in "Plugin Configuration File") as well, <synonym> ("synonym" in "Plugin Configuration File") provides alternative names when searching for actions, and groups can be excluded from search results.
To provide additional custom tabs, implement WelcomeTabFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/WelcomeTabFactory.java) and register in com.intellij.welcomeTabFactory extension point.
To control file type association with the IDE in the operating system, implement OSFileIdeAssociation (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/fileTypes/OSFileIdeAssociation.java).
Implement ReaderModeProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/codeInsight/actions/ReaderModeProvider.kt) and register in com.intellij.readerModeProvider extension point to apply custom settings for files rendered in reader mode. Provide com.intellij.codeInsight.actions.ReaderModeMatcher to disable Reader Mode for particular set of files.
Implement TextEditorCustomizer (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorCustomizer.java) and register in com.intellij.textEditorCustomizer extension point to customize created editors.
This allows using existing test base classes, see WebStorm Plugin Development ("Javascript Test Framework" in "WebStorm Plugin Development") page for details.
When targeting 2020.3, please see this migration guide (https://blog.jetbrains.com/clion/2020/12/migration-guide-for-plugins-2020-3/).
Please obtain necessary components only when needed (logged as ERROR now).
Added VirtualFileManager.findFileByNioPath()/refreshAndFindFileByNioPath(). See also VirtualFile.toNioPath().
Register resource bundle via com.intellij.iconDescriptionBundle extension point to provide tooltips automatically for all SimpleColoredComponent (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java) renderers.
A plugin can mark itself incompatible ("Declaring Incompatibility with Module" in "Plugin Compatibility with IntelliJ Platform Products") if IDE contains specified module.
To support on-the-fly Editor color scheme switching, change calls from methods taking TextAttributes.
The platform now bundles support for images in WebP (https://en.wikipedia.org/wiki/WebP) format.
Specify hashBangs attribute in com.intellij.fileType extension point. Issue (https://youtrack.jetbrains.com/issue/IDEA-175757)
Implement AboutPopupDescriptionProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-impl/src/com/intellij/ide/AboutPopupDescriptionProvider.kt) and register in com.intellij.aboutPopupDescriptionProvider extension point.
To support preview in intention popup, suitable FileModifier (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/analysis-api/src/com/intellij/codeInsight/intention/FileModifier.java) must be provided (default implementation FileModifier.getFileModifierForPreview() works for most cases).
Switch to matching IDE feature by implementing TerminalShellCommandHandler (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/execution-impl/src/com/intellij/terminal/TerminalShellCommandHandler.kt) (com.intellij.terminal.shellCommandHandler extension point). Blog post (https://blog.jetbrains.com/idea/2020/07/run-ide-features-from-the-terminal/)
We recommend switching to JCEF (Embedded Browser (JCEF)), please see blog post (https://blog.jetbrains.com/platform/2020/07/javafx-and-jcef-in-the-intellij-platform/) for details.
Updated from 7.0.1.
Lexer is wrapped using ValidatingLexerWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-ex/src/com/intellij/openapi/editor/ex/util/ValidatingLexerWrapper.java) to verify it generates a continuous sequence of tokens and doesn't stall during iteration.
Several plugins (ASP, CFML, Flash/Flex, GWT, JBoss Seam Pageflow, JBoss Seam, JBoss jBPM, OSGi, Play Framework, Resin, Seam Navigation, Tapestry, Virgo/dmServer) for no longer actively maintained technology have been unbundled. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Compatible plugins (Dynamic Plugins) can be installed, updated and uninstalled without requiring IDE restart.
Usage is deprecated and can be replaced with com.intellij.openapi.application.Application.invokeLater() in most cases, please consult Javadoc for more details.
Please see RecursionManager.CachingPreventedException (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/RecursionManager.java) Javadoc and this issue (https://youtrack.jetbrains.com/issue/IDEA-228809) for details.
Reports when the same reference resolves non-equivalent results in different threads, see IdempotenceChecker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/util/IdempotenceChecker.java).
Set addOpenInEditorCheckbox constructor parameter to enable it in custom RefactoringDialog implementation.
Use com.intellij.statusBarWidgetFactory extension point to provide widgets that can be disabled or reordered, see Status Bar Widgets.
Allows embedding (Embedded Browser (JCEF)) Chromium-based browser in the IDE.
Set the <override-text> ("Setting the override-text Element" in "Actions") element within the <action> ("action" in "Plugin Configuration File") declaration in plugin.xml (Plugin Configuration File).
Import from Existing Sources has been removed from the Welcome Screen, leaving only Open or Import, which calls a different extension than the one previously used to contribute a wizard step to Import from Existing Sources (which is still available in the File menu). To support Open or Import, a plugin must provide ProjectOpenProcessor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/projectImport/ProjectOpenProcessor.kt). ProjectOpenProcessor.canOpenProject() should return true for the folder selected by the user only if it guarantees doOpenProject() can handle it. If there are several matching processors, a simple chooser dialog is shown. If additional manual configuration is necessary, a modal dialog can be shown in doOpenProject() - however, it is highly recommended performing all setup automatically (like Maven and Gradle plugins do).
Please use Azure DevOps (https://plugins.jetbrains.com/plugin/7981-azure-devops) plugin instead, see blog post (https://blog.jetbrains.com/idea/2020/01/end-of-support-for-tfs-2014-and-older/) for more details.
Several plugins (Cloud Foundry, Google App Engine) for no longer actively maintained technology have been unbundled. If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Now reflects its "heavy test" characteristics (see Light and Heavy Tests (Light and Heavy Tests)).
Optional plugin.xml configuration files ("Additional Plugin Configuration Files" in "Plugin Configuration File") can now specify <depends> ("depends" in "Plugin Configuration File"). Issue (https://youtrack.jetbrains.com/issue/IDEA-209769)
(Un)Installing or enabling/disabling Theme (Getting Started) or Keymap (https://plugins.jetbrains.com/search?tags=Keymap) plugins doesn't require an IDE restart anymore.
Use com.intellij.ide.util.RunOnceUtil to run a task exactly once for application or per project.
Contribute symbol names (classes, methods, ..) via com.intellij.completion.plainTextSymbol extension point (com.intellij.codeInsight.completion.PlainTextSymbolCompletionContributor).
Use com.intellij.util.text.JBDateFormat#getFormatter() to use configured format from Settings | Appearance & Behavior | System Settings | Date Formats.
Several plugins (Heroku integration, RubyMotion support, Java Applets Support) for no longer actively maintained technology have been moved to a separate repository (https://github.com/JetBrains/intellij-obsolete-plugins/). If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Consistent with other quickfixes, the menu now shows names of fixes, not names of problems themselves. Issue (https://youtrack.jetbrains.com/issue/IDEA-216731)
Use com.intellij.openapi.editor.richcopy.HtmlSyntaxInfoUtil to create Lexer-based highlighted code samples, e.g., for usage in documentation.
Toggles showing additional details in UI (e.g. modification timestamp in Project View) see UISettings.getShowInplaceComments().
Allows a variety of presentations (incl. custom painting), mouse event handling, and exposing settings in Settings | Editor | Inlay Hints. See com.intellij.codeInsight.hints.InlayHintsProvider.
A non-blocking variant for com.intellij.openapi.vfs.newvfs.BulkFileListener.
Provides background color in all Trees, Lists and ComboBoxes.
Use new dedicated com.intellij.backgroundPostStartupActivity extension point (see Javadoc for StartupActivity#BACKGROUND_POST_STARTUP_ACTIVITY).
Use new dedicated com.intellij.highlightingPassFactory extension point.
When registering a file type via file extension, pattern or exact file name matching, use com.intellij.fileType extension point instead (see Sample ("Register the File Type" in "2. Language and File Type")).
Indicates that the annotated API class, interface, or method must not get extended, implemented, or overridden by external plugins but can only be obtained or instantiated for classes and interfaces, or called for methods.
Indicates that the annotated method is part of SPI (Service Provider Interface), which is intended to be only implemented or overridden but never called by external plugins.
Defaults to thread-safe to prevent problems with clients using unsynchronized collections.
Returns dedicated message when invalid SDK path was chosen (e.g., JRE instead of JDK).
If your plugin depends on Java, it must be specified now; see "Java" in "Plugin Compatibility with IntelliJ Platform Products" for details.
Several plugins (J2ME, JsTestDriver, Struts 1.x) for no longer actively maintained technology have been moved to a separate repository (https://github.com/JetBrains/intellij-obsolete-plugins/). If your plugin depends on them, users will need to install them from the JetBrains Marketplace (https://plugins.jetbrains.com).
Use dedicated ProjectDescriptor or rollback project setup changes in tearDown() (see Light and Heavy Tests (Light and Heavy Tests)).
External annotations for the IntelliJ Platform are generated and attached to plugin projects automatically (replacing @since Javadoc).
External annotations for the IntelliJ Platform are generated and attached to plugin projects automatically. This allows highlighting of API, which has been removed in newer platform versions.
Indicates that the annotated element must not be considered as a public API. Do not use outside of the IntelliJ Platform, see Internal API Migration.
Assert references are created for the given underlying PsiElement. Issue (https://youtrack.jetbrains.com/issue/IDEA-203954)
Enabled in tests and EAP/internal mode, see CachedValueStabilityChecker (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-impl/src/com/intellij/util/CachedValueStabilityChecker.java) Javadoc.
This page lists commonly used API annotated with ApiStatus.@Internal (https://github.com/JetBrains/java-annotations/tree/master//common/src/main/java/org/jetbrains/annotations/ApiStatus.java) or @IntellijInternalApi (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/util/src/com/intellij/openapi/util/IntellijInternalApi.kt) which indicates it is private API and must not be used outside of IntelliJ Platform itself:
`ApiStatus.Internal JavadocIndicates that the annotated element (class, method, field, etc.) must not be considered as a public API. It's made visible to allow usages in other packages of the declaring library, but it must not be used outside of that library. Such elements may be renamed, changed, or removed in future versions.
Such violations are reported from "Plugin Verifier" in "Verifying Plugin Compatibility" and are highlighted in the IDE using dedicated inspection ("IDE Support" in "Verifying Plugin Compatibility").
Each entry is mapped to its corresponding Replacement, pointing to the recommended API.
The lists are not complete and will be updated continuously. Please check corresponding code documentation when encountering any API not listed on this page.
In some cases, such documentation might not be available inside the IDE for the current target platform version. Please use Go to file to browse the latest version in the intellij-community (https://github.com/jetbrains/intellij-community) GitHub repository instead.
Please use the feedback form at the bottom of this page if you encounter missing or unclear information.
Internal API | Replacement |
|---|---|
AnAction.applyTextOverride() | |
ApplicationLoadListener | |
BuildNumber.currentVersion() | Use ApplicationInfo.getBuild() |
CompactVirtualFileSet | Use VfsUtilCore.createCompactVirtualFileSet() |
DefaultPicoContainer | Use extension points (Extensions) and services (Services) |
EdtDataContext | |
ExperimentalUI.isNewUI() | Use NewUI.isEnabled() (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/ui/NewUI.java) |
FileTypeIndex.NAME | Use static methods in FileTypeIndex directly |
IElementType.getDebugName() | Override/use IElementType.toString() |
IconLoader.CachedImageIcon | Use methods exposed in IconLoader |
IconLoader.LazyIcon | Use IconLoader.createLazy() |
IndexingDataKeys | |
Module.getModuleFile() | |
Module.getModuleFilePath() | |
Module.getModuleTypeName() | |
ModuleTypeManager.registerModuleType() | Use com.intellij.moduleType extension point instead, ModuleType (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/lang-core/src/com/intellij/openapi/module/ModuleType.java) |
PathMacros.setMacro() | Use com.intellij.pathMacroContributor extension point, PathMacroContributor (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/core-api/src/com/intellij/openapi/application/PathMacroContributor.java) |
PlatformUtils | |
PluginClassLoader | Cast to PluginAwareClassLoader (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/extensions/src/com/intellij/ide/plugins/cl/PluginAwareClassLoader.java) |
PluginManager.getLogger() | Use own logger, see "Logging" in "IDE Infrastructure" |
PreloadingActivity | Use StartupActivity.Background (docs ("Project Open" in "Components")) with atomic flag to run only once during IDE lifetime |
ProjectLibraryTable | Use LibraryTablesRegistrar.getLibraryTable() |
SVGLoader | Use ImageLoader.loadFromResource() |
ScrollBarPainter | |
UtilKt.targetPresentation() |
Internal API | Replacement |
|---|---|
DbDataSource.getDelegate() | For connection config use DbDataSource.getConnectionConfig(), for LocalDataSource use DbImplUtil.getMaybeLocalDataSource(DasDataSource) |
The API listed in this table is currently (or was previously) marked with ApiStatus.@Internal, but its status has changed in the meantime (or will change). Therefore, any reported violations can be disregarded.
The lists are not complete and will be updated continuously. Please check corresponding code documentation when encountering any API not listed on this page.
In some cases, such documentation might not be available inside the IDE for the current target platform version. Please use Go to file to browse the latest version in the intellij-community (https://github.com/jetbrains/intellij-community) GitHub repository instead.
Please use the feedback form at the bottom of this page if you encounter missing or unclear information.
Current Release: 1.17.3
GitHub: Releases & Changelog (https://github.com/JetBrains/gradle-intellij-plugin/releases), Issue Tracker (https://github.com/JetBrains/gradle-intellij-plugin/issues)
The Gradle IntelliJ Plugin is a plugin for the Gradle build system to help configure your environment for building, testing, verifying, and publishing plugins for IntelliJ-based IDEs.
This plugin allows you to build plugins for IntelliJ Platform using specified IntelliJ SDK and bundled or third-party plugins.
The plugin provides functionalities like:
adding extra IntelliJ-specific dependencies
patching processResources tasks to fill some tags (name, version) in plugin.xml (Plugin Configuration File) with appropriate values
patching compile tasks to instrument code with nullability assertions and form classes made with IntelliJ GUI Designer
additional build steps that are helpful for developing plugins for the IntelliJ Platform
Please see also Gradle IntelliJ Plugin – FAQ and Gradle IntelliJ Plugin – Usage Examples.
Before visiting the Issue Tracker (https://github.com/JetBrains/gradle-intellij-plugin/issues), update both plugin and Gradle to the latest versions. Please see CONTRIBUTING (https://github.com/JetBrains/gradle-intellij-plugin/blob/master/CONTRIBUTING.md) on how to submit feedback and contribute to this project.
IntelliJ Platform Gradle Plugin 2.x (Beta) (currently in Early Access Preview) is going to replace this plugin in the future. Please note that production usage is currently not recommended yet.
Gradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
To enable this plugin in your Gradle-based project, register the plugin in the Gradle build script's plugins section:
plugins {
id("org.jetbrains.intellij") version "1.17.3"
}plugins {
id "org.jetbrains.intellij" version "1.17.3"
}When upgrading to 1.x version, please make sure to follow the migration guide (https://lp.jetbrains.com/gradle-intellij-plugin) to adjust your existing build script.
Minimum Gradle VersionThis project requires Gradle 7.3 or newer. However, it is recommended to use the latest Gradle available. See Gradle Installation (https://gradle.org/install/) guide.
Some additional settings are required in the IDE after setting up the plugin.
Gradle JVM must be set to Java 11 in Settings | Build, Execution, Deployment | Build Tools | Gradle. When targeting 2022.3+, Java 17 is required instead (see details ("Platform Versions" in "Build Number Ranges")).
This step is not required when using Gradle IntelliJ Plugin version 1.17.2 or later.
To attach IntelliJ Platform sources in the IDE when enabled via some additional settings are required in IDE versions 2023.2 and later.
In Settings | Advanced Settings enable option Download sources in section Build Tools. Gradle. Then invoke Reload All Gradle Projects action from the Gradle tool window.
In Settings | Build, Execution, Deployment | Build Tools | Gradle enable Download sources for dependencies. Then invoke Reload All Gradle Projects action from the Gradle tool window.
No additional IDE settings are required.
The Snapshot release is a pre-release version built nightly from the latest main branch – as it is built every day using the same version number, it's not recommended to use it for production builds.
To switch to the snapshot release, point Gradle to the dedicated snapshot repository by adding an entry to the Gradle settings file.
The current Gradle IntelliJ Plugin Snapshot version is
To make sure you obtain the latest snapshot version, invoke Gradle using --refresh-dependencies option.
build.gradle.kts
plugins {
id("org.jetbrains.intellij") version "..."
}settings.gradle.kts
pluginManagement {
repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots/")
gradlePluginPortal()
}
}build.gradle
plugins {
id "org.jetbrains.intellij" version "..."
}settings.gradle
pluginManagement {
repositories {
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
gradlePluginPortal()
}
}The Gradle build system brings the Configuration Cache (https://docs.gradle.org/current/userguide/configuration_cache.html) feature that helps improve the build performance by caching the configuration phase.
The Gradle IntelliJ Plugin is fully compatible with this mechanism and can be utilized by manual enabling of the Configuration Cache feature with the --configuration-cache flag, like:
gradle buildPlugin --configuration-cacheor by enabling it in the gradle.properties file:
org.gradle.unsafe.configuration-cache = trueSee Using the configuration cache (https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage) in the Gradle documentation for more details.
Sometimes, you may want to split your plugin into multiple modules — i.e., to separate the core plugin code from the code related to other third-party plugin dependencies. The most common way to do this is to use the Gradle Multi-Project Build (https://docs.gradle.org/current/userguide/multi_project_builds.html) feature.
This approach allows you to declare dependencies between subprojects, like:
dependencies {
implementation(project(":shared"))
}dependencies {
implementation project(':shared')
}Because the Gradle IntelliJ Plugin introduces the code instrumentation, and the default implementation configuration is not compatible with it, you need to specify the instrumentedJar configuration explicitly to refer to the instrumented JAR file produced by the plugin instead of the default JAR file:
dependencies {
implementation(project(":shared", "instrumentedJar"))
}dependencies {
implementation project(path: ':shared', configuration: 'instrumentedJar')
}After the Gradle IntelliJ Plugin is applied, the intellij extension can be used to configure the plugin and common settings of the provided tasks.
It is mandatory to specify at least the intellij.version property.
Example:
intellij {
version.set("2022.1.1")
type.set("IU")
plugins.set(listOf("com.jetbrains.php:221.5787.21"))
}intellij {
version = "2022.1.1"
type = "IU"
plugins = ["com.jetbrains.php:221.5787.21"]
}All available JetBrains IDEs versions can be found in the repositories described on the IntelliJ Platform Artifacts Repositories page.
The version of the IntelliJ Platform IDE that will be used to build the plugin. Please see Plugin Compatibility with IntelliJ Platform Products and Build Number Ranges for more details.
true
String
version number: 2022.1.1 or IC-2022.1.1
build number: 221.5080.210 or IC-221.5080.210
snapshot: 221-EAP-SNAPSHOT or LATEST-EAP-SNAPSHOT
The version number format is the most common option for specifying the version of the IntelliJ Platform. Other formats should be used only when your plugin relies on specific parts of the targeted IDE or early-adopting EAP releases.
The type of the IntelliJ-based IDE distribution. The type may also be specified as a prefix of the value for the intellij.version property instead.
String
IC
IC - IntelliJ IDEA Community Edition (IntelliJ IDEA Plugin Development)
IU - IntelliJ IDEA Ultimate Edition (IntelliJ IDEA Plugin Development)
CL - CLion (CLion Plugin Development)
PY - PyCharm Professional Edition (PyCharm Plugin Development)
PC - PyCharm Community Edition (PyCharm Plugin Development)
PS - PhpStorm (PhpStorm Plugin Development)
RD - Rider (Rider Plugin Development)
GO - GoLand (GoLand Plugin Development)
AI - Android Studio (Android Studio Plugin Development)
RR - Rust Rover (https://plugins.jetbrains.com/plugin/22407-rust/edit)
JPS - JPS-only
GW - Gateway
To build against IDEs not supported directly by type, please see their corresponding page in Product Specific.
The plugin name part used in the generated ZIP distribution: build/distributions/PluginName-1.0.0.zip, and the name of the plugin directory in the sandbox directory.
String
${project.name}
The path to the locally installed IDE distribution that should be used to build the plugin. Using intellij.localPath allows building the plugin using an IDE that is not available in IntelliJ Platform Artifacts Repositories.
String
null
Windows: C:\Users\user\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\211.7142.45
macOS: /Applications/Android Studio 4.2 Preview.app/Contents (note /Contents suffix)
Linux: /home/user/idea-IC-181.4445.78
intellij.version and intellij.localPath must not be specified at the same time.
The path to local archive with IDE sources. Used for resolving source files of the locally installed IDE distribution when intellij.localPath is specified.
String
null
The list of bundled IDE plugins and plugins from JetBrains Marketplace (https://plugins.jetbrains.com) or configured intellij.pluginsRepositories.
Please see Plugin Dependencies for more details.
Notes:
For plugins from JetBrains Marketplace (https://plugins.jetbrains.com), use the pluginId:version format, like org.intellij.plugins.markdown:231.8109.126. The version of your choice must be compatible with the version of the IDE you're building against.
For bundled plugins, use the plugin ID with the version part omitted, like: org.intellij.groovy.
For subprojects, use project reference project(':subproject', 'instrumentedJar').
For plugin built locally, pass the path to the lib/ directory of the extracted plugin archive, like: file("/path/to/plugin/lib/") or file("/projects/plugin-name/build/idea-sandbox/plugins/plugin-name/lib/"). See "How to add a dependency on a plugin available in the file system?" in "Gradle IntelliJ Plugin – FAQ".
If you need to refer plugin's classes from your project, you also have to define a dependency in your plugin.xml (Plugin Configuration File) file, see Plugin Dependencies.
List<Any>
[]
org.plugin.id:version[@channel] format, String type:
org.intellij.plugins.markdown:8.5.0
org.intellij.scala:2017.2.638@nightly
bundledPluginId format, String type:
org.intellij.groovy
project(...) format, Project type:
project(":projectName", "instrumentedJar")
project(":plugin-subproject", "instrumentedJar")
file(...) format, File type:
file("/path/to/plugin/lib/")
file("/projects/plugin-name/build/idea-sandbox/plugins/plugin-name/lib/")
Enables patching plugin.xml (Plugin Configuration File) with the values of patchPluginXml.sinceBuild and patchPluginXml.untilBuild properties.
Boolean
true
Enables patching plugin.xml (Plugin Configuration File) with the patchPluginXml.untilBuild using value of patchPluginXml.sinceBuild with * wildcard, like sinceBuild.*, e.g., 221.*.
Notes:
Useful for building plugins against EAP builds.
If patchPluginXml.untilBuild has a value set, then intellij.sameSinceUntilBuild is ignored.
Boolean
false
Enables the instrumentation of Java classes with nullability (https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html) assertions and compilation of forms created by IntelliJ GUI Designer (https://www.jetbrains.com/help/idea/gui-designer-basics.html).
Boolean
true
The path of sandbox directory ("The Development Instance Sandbox Directory" in "IDE Development Instance") that is used for running IDE with developed plugin.
String
${project.buildDir}/idea-sandbox
The IntelliJ-based IDE distributions repository URL.
String
https://cache-redirector.jetbrains.com/www.jetbrains.com/intellij-repository
Configures repositories for downloading plugin dependencies. See Maven Interface (https://plugins.jetbrains.com/docs/marketplace/maven-interface.html) for details on Maven repository format.
PluginsRepositoryConfiguration
pluginsRepositories { marketplace() }
marketplace() - use Maven repository with plugins listed in JetBrains Marketplace (https://plugins.jetbrains.com)
maven(repositoryUrl) - use custom Maven repository with plugins
maven { repositoryUrl } - use custom Maven repository with plugins where you can configure additional parameters (credentials, authentication, etc.)
custom(pluginsXmlUrl) - use Custom Plugin Repository
URL of repository for downloading JetBrains Runtime ("Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance").
String
null
Path to the directory where the IDE dependency cache is stored. If not set, the dependency will be extracted next to the downloaded ZIP archive in Gradle cache (https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home) directory.
String
null
Enables downloading the IntelliJ Platform sources. It is enabled by default if the CI environment variable is not set – which is present in Continuous Integration environments, like GitHub Actions, TeamCity, and others.
Boolean
!System.getenv().containsKey("CI")
Enables configuration of the default IntelliJ Platform dependencies in the current project. Otherwise, the DependenciesUtils.intellij(), DependenciesUtils.intellijPlugin(), and DependenciesUtils.intellijPlugins() functions could be used for an explicit configuration.
Boolean
true
Configure extra dependency artifacts from the IntelliJ repository. The dependencies on them could be configured only explicitly using the DependenciesUtils.intellijExtra() function in the dependencies block.
List<String>
[]
List of dependencies on external plugins.
List<PluginDependency>
[]
Assembles a plugin and prepares ZIP archive for deployment (Publishing a Plugin).
buildPlugin task extends the Zip (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html) Gradle task.
Do not repackage libraries into the main plugin archive, see "Plugin With Dependencies" in "Plugin Content".
The base name of the ZIP archive.
This task is preconfigured automatically and takes the output artifacts of prepareSandbox and jarSearchableOptions tasks as an input.
String
${prepareSandboxTask.pluginName}
Builds an index of UI components (searchable options) for the plugin. This task runs a headless IDE instance to collect all the available options provided by the plugin's Settings.
Note, this is a runIde-based task with predefined arguments and all properties of the runIde task are also applied to buildSearchableOptions tasks.
If your plugin doesn't implement custom settings, it is recommended to disable it ("How to disable building searchable options?" in "Gradle IntelliJ Plugin – FAQ"). See also noSearchableOptionsWarning ("noSearchableOptionsWarning" in "Gradle IntelliJ Plugin – Build Features") build feature.
File
build/searchableOptions
Remove classpath.index files that are created by the PathClassLoader. This loader, due to the implementation bug, ignores the idea.classpath.index.enabled=false flag and as a workaround, files have to be removed manually.
Task is enabled if intellij.version is set to 2022.1 or higher.
The list of classpath.index files to be removed.
ConfigurableFileCollection
List of classpath.index files resolved with sourceSets configuration
Download the robot-server plugin. The robot-server plugin is required for running the UI tests using the runIdeForUiTests task.
The version of the Robot Server Plugin to download.
String
LATEST
The Robot Server Plugin archive, downloaded by default to the Gradle cache (https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home).
File
Gradle cache (https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home)
Location of the extracted archive.
File
build/robotServerPlugin
Resolves and downloads Marketplace ZIP Signer CLI tool used by the signPlugin task.
Version of the ZIP Signer CLI tool to download.
String
LATEST
Path to the ZIP Signer CLI tool.
File
Gradle cache (https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home)
The output of the ZIP Signer CLI tool.
File
cliPath
Initializes the Gradle IntelliJ Plugin and performs various checks, like if the plugin is up-to-date.
The following attributes help you to tune instrumenting behavior in the instrumentCode { ... } block.
The dependency on IntelliJ IDEA.
IdeaDependency
intellij.ideaDependency
Path to the javac2.jar file of IntelliJ IDEA.
File
lib/javac2.jar resolved in instrumentCode.ideaDependency
A version of instrumenting compiler. It's used in cases when targeting non-IntelliJ IDEA IDEs (e.g., CLion (CLion Plugin Development) or Rider (Rider Plugin Development)).
String
Build number of the IDE dependency
The list of directories with compiled classes.
FileCollection
sourceSets.[].output.classesDirs
The list of directories with GUI Designer form files.
FileCollection
.form files of the project's source sets.
The output directory for instrumented classes.
File
setupInstrumentCode.instrumentedDir
The classpath for Java instrumentation compiler.
FileCollection
Create a JAR file with instrumented classes.
Create a JAR file with searchable options to be distributed with the plugin.
The output directory where the JAR file will be created.
String
build/searchableOptions
The name of the plugin.
String
intellij.pluginName
The sandbox output directory.
String
prepareSandbox.outputDir
Lists all IDs of plugins bundled within the currently targeted IDE. This can be used to determine Plugin ID for setting up Plugin Dependencies.
See also .
The IDE dependency sources path. Configured automatically with the setupDependencies.idea dependency.
File
setupDependencies.idea
Path to the file, where the output list will be stored.
File
File("${project.buildDir}/listBundledPlugins.txt")
List all available IntelliJ-based IDE releases with their updates. The result list is used for testing the plugin with Plugin Verifier using the runPluginVerifier task.
Plugin Verifier requires a list of the IDEs that will be used for verifying your plugin build against. The availability of the releases may change in time, i.e., due to security issues in one version – which will be later removed and replaced with an updated IDE release.
With the listProductsReleases task, it is possible to list the currently available IDEs matching given conditions, like platform types, since/until release versions. Such a list is fetched from the remote updates file: https://www.jetbrains.com/updates/updates.xml, parsed and filtered considering the specified listProductsReleases.types, listProductsReleases.sinceVersion, listProductsReleases.untilVersion (or listProductsReleases.sinceBuild, listProductsReleases.untilBuild) properties.
The result list is stored within the listProductsReleases.outputFile, which is used as a source for the Plugin Verifier if the runPluginVerifier task has no runPluginVerifier.ideVersions property specified, the output of the listProductsReleases task is used.
See also .
Path to the products releases update files. By default, one is downloaded from IntelliJPluginConstants.IDEA_PRODUCTS_RELEASES_URL.
FileCollection
Gradle cache (https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home)
List of types of IDEs that will be listed in results.
List<String>
intellij.type
Lower boundary of the listed results in product marketing version format, e.g., 2020.2.1. It takes precedence over the listProductsReleases.sinceBuild property.
String
intellij.version
Upper boundary of the listed results in product marketing version format, e.g., 2020.2.1. It takes precedence over the listProductsReleases.untilBuild property.
String
null
Lower boundary of the listed results in build number format, like 192.
String
intellij.version
Upper boundary of the listed results in build number format, like 192.
String
null
Release channels that product updates will be filtered with.
Channel
EnumSet.allOf(ListProductsReleasesTask.Channel)
Path to the file, where the output list will be stored.
File
File("${project.buildDir}/listProductsReleases.txt")
For Android Studio releases (Android Studio Releases List), a separated storage for the updates is used.
String
https://raw.githubusercontent.com/JetBrains/intellij-sdk-docs/main/topics/_generated/android_studio_releases.xml
Patches plugin.xml (Plugin Configuration File) files with values provided to the task.
To maintain and generate an up-to-date changelog, try using the Gradle Changelog Plugin (https://github.com/JetBrains/gradle-changelog-plugin).
The directory where the patched plugin.xml (Plugin Configuration File) will be written.
String
${project.buildDir}/patchedPluginXmlFiles
The list of plugin.xml (Plugin Configuration File) files to patch.
List<File>
auto-discovered from the project
The description of the plugin used in the <description> ("description" in "Plugin Configuration File") tag.
String
null
The lower bound of the version range (Build Number Ranges) to be patched used in the since-build attribute of the <idea-version> ("idea-version" in "Plugin Configuration File") tag.
String
intellij.version in Branch.Build.Fix format
The upper bound of the version range (Build Number Ranges) to be patched used in the until-build attribute of the <idea-version> ("idea-version" in "Plugin Configuration File") tag.
String
intellij.version in Branch.Build.* format
The version of the plugin used in the <version> ("version" in "Plugin Configuration File") tag.
String
${project.version}
The change notes of the plugin used in the <change-notes> ("change-notes" in "Plugin Configuration File") tag.
String
null
The ID of the plugin used in the <id> ("id" in "Plugin Configuration File") tag.
String
null
Prepares the sandbox directory with the installed plugin and its dependencies.
The name of the plugin.
String
intellij.pluginName
The directory with the plugin configuration.
String
${intellij.pluginName}/config
The input plugin JAR file used to prepare the sandbox.
File
output of the jar task
Libraries that will be ignored when preparing the sandbox. By default, it excludes all libraries that are a part of the setupDependenciesTask.idea dependency.
List<File>
org.jetbrains.intellij.tasks.SetupDependenciesTask.idea.get().jarFiles
List of dependencies on external plugins.
List<PluginDependency>
org.jetbrains.intellij.IntelliJPluginExtension.getPluginDependenciesList
Prepares the sandbox directory with the installed plugin and its dependencies for testing purposes.
See prepareSandbox Task.
Prepares the sandbox directory with the installed plugin and its dependencies for UI testing purposes.
See prepareSandbox Task.
Print the output produced by the listProductsReleases task.
Output file provided by the listProductsReleases task.
Print the output produced by the listBundledPlugins task.
Output file provided by the listBundledPlugins task.
Publishes plugin to the remote JetBrains Marketplace (https://plugins.jetbrains.com) repository.
The following attributes are a part of the Publishing DSL publishPlugin { ... } in which allows Gradle to upload plugin to JetBrains Marketplace (https://plugins.jetbrains.com). Note that you need to upload the plugin ("Uploading a Plugin to JetBrains Marketplace" in "Publishing a Plugin") to the repository at least once manually (to specify options like the license, repository URL, etc.) before uploads through Gradle can be used.
See the instruction on how to generate authentication token (https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html).
See the "Publishing Plugin With Gradle" in "Publishing a Plugin" tutorial for step-by-step instructions.
Authentication token.
Required
String
null
List of channel names to upload plugin to.
List<String>
["default"]
Mark the release as hidden to prevent public release after approval. See Hidden release (https://plugins.jetbrains.com/docs/marketplace/hidden-plugin.html) in JetBrains Marketplace docs.
Boolean
false
URL host of a plugin repository.
String
JetBrains Marketplace (https://plugins.jetbrains.com)
ZIP file of plugin to upload.
File
output of the buildPlugin task
Specify if the Toolbox Enterprise plugin repository service should be used. This feature is still in the incubating phase and is not yet available for public use.
Boolean
false
Run the IDE instance with the developed plugin installed.
runIde task extends the JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) Gradle task – all properties available in the JavaExec as well as the following ones can be used to configure the runIde task.
The IDE dependency sources path. Configured automatically with the setupDependencies.idea dependency.
File
setupDependencies.idea
Custom JetBrains Runtime (JBR) version to use for running the IDE.
String
null
8u112b752.4
8u202b1483.24
11_0_2b159
For more information about JBR versions and variants, see "Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance".
JetBrains Runtime (JBR) variant to use when running the IDE with the plugin.
String
null
JetBrains Runtime architecture. By default, it's resolved based on the current OS and JRE architecture.
String
null
Path to the plugins directory within the sandbox prepared with the prepareSandbox task. Provided to the idea.plugins.path system property.
Directory
prepareSandbox.destinationDir
Enables auto-reload of dynamic plugins. Dynamic plugins will be reloaded automatically when their JARs are modified. This allows a much faster development cycle by avoiding a full restart of the development instance after code changes. Enabled by default in 2020.2 and higher.
See Enabling Auto-Reload ("Enabling Auto-Reload" in "IDE Development Instance") for more details.
Boolean
true
Run the IDE instance with the developed plugin and robot-server installed and ready for UI testing.
See intellij-ui-test-robot (https://github.com/JetBrains/intellij-ui-test-robot) project.
See runIde task for more details.
Run performance tests on the IDE with the developed plugin installed.
The runIdePerformanceTest task extends the RunIdeBase task, so all configuration attributes of JavaExec and runIde tasks can be used in the runIdePerformanceTest as well. See runIde task for more details.
Currently, the task is under adaptation; more documentation will be added in the future.
Path to directory with test projects and .ijperf files.
String
null
Path to the directory where performance test artifacts (IDE logs, snapshots, screenshots, etc.) will be stored. If the directory doesn't exist, it will be created.
String
null
Name of the profiler which will be used during execution.
ProfilerName
ProfilerName.ASYNC
ProfilerName.ASYNC
ProfilerName.YOURKIT
Run the IntelliJ Plugin Verifier (https://github.com/JetBrains/intellij-plugin-verifier) tool to check the binary compatibility with specified IDE builds (see also Verifying Plugin Compatibility).
Plugin Verifier DSL runPluginVerifier { ... } allows to define the list of IDEs used for the verification, as well as explicit tool version and any of the available options (https://github.com/JetBrains/intellij-plugin-verifier#common-options) by proxifying them to the Verifier CLI.
For more details, examples or issues reporting, go to the IntelliJ Plugin Verifier (https://github.com/JetBrains/intellij-plugin-verifier) repository.
To run Plugin Verifier in -offline (https://github.com/JetBrains/intellij-plugin-verifier/pull/58) mode, set the Gradle offline start parameter (https://docs.gradle.org/current/javadoc/org/gradle/StartParameter.html#setOffline-boolean-).
The IDEs to be checked in intellij.version format, i.e.: ["IC-2019.3.5", "PS-2019.3.2"]. Check the available build versions on IntelliJ Platform Builds list (https://jb.gg/intellij-platform-builds-list).
List<String>
output of the listProductsReleases task
IntelliJ Plugin Verifier version. Do not change unless absolutely required.
String
LATEST
Local path to the pre-downloaded IntelliJ Plugin Verifier JAR file. If set, runPluginVerifier.verifierVersion is ignored.
String
path to the JAR file resolved using the runPluginVerifier.verifierVersion property
A list of the paths to locally installed IDE distributions that should be used for verification in addition to those specified in runPluginVerifier.ideVersions.
List<File>
[]
ZIP file of the plugin to verify. If empty, the task will be skipped.
File
output of the buildPlugin task
Defines the verification level at which the task should fail if any reported issue matches. Can be set as FailureLevel enum or EnumSet<FailureLevel>.
org.jetbrains.intellij.tasks.RunPluginVerifierTask.FailureLevel
FailureLevel.COMPATIBILITY_PROBLEMS
FailureLevel.COMPATIBILITY_WARNINGS - Compatibility warnings detected against the specified IDE version.
FailureLevel.COMPATIBILITY_PROBLEMS - Compatibility problems detected against the specified IDE version.
FailureLevel.DEPRECATED_API_USAGES - Plugin uses API marked as deprecated (@Deprecated).
FailureLevel.SCHEDULED_FOR_REMOVAL_API_USAGES - Plugin uses API marked as scheduled for removal (ApiStatus.@ScheduledForRemoval).
FailureLevel.EXPERIMENTAL_API_USAGES - Plugin uses API marked as experimental (ApiStatus.@Experimental).
FailureLevel.INTERNAL_API_USAGES - Plugin uses API marked as internal (ApiStatus.@Internal).
FailureLevel.OVERRIDE_ONLY_API_USAGES - Override-only API is used incorrectly (ApiStatus.@OverrideOnly).
FailureLevel.NON_EXTENDABLE_API_USAGES - Non-extendable API is used incorrectly (ApiStatus.@NonExtendable).
FailureLevel.PLUGIN_STRUCTURE_WARNINGS - The structure of the plugin is not valid.
FailureLevel.MISSING_DEPENDENCIES - Plugin has some dependencies missing.
FailureLevel.INVALID_PLUGIN - "Provided plugin artifact is not valid."
FailureLevel.NOT_DYNAMIC - "Plugin cannot be loaded/unloaded without IDE restart."
FailureLevel.ALL - All of the above
FailureLevel.NONE - None of the above
The path to the directory where verification reports will be saved.
String
${project.buildDir}/reports/pluginVerifier
The output formats of the verification reports that will be emitted.
List<String>
["plain", "html"]
plain (console output)
html (HTML format)
markdown (Markdown format)
The path to the directory where IDEs used for the verification will be downloaded. By default, it relies on the plugin.verifier.home.dir system property and falls back to the XDG_CACHE_HOME environment variable – see XDG Base Directory (https://wiki.archlinux.org/title/XDG_Base_Directory) for more details.
String
System.getProperty("plugin.verifier.home.dir")/ides, System.getenv("XDG_CACHE_HOME")/pluginVerifier/ides, System.getProperty("user.home")/.cache/pluginVerifier/ides or system temporary directory.
Custom JetBrains Runtime (JBR) version to use for running the verification.
String
null
8u112b752.4
8u202b1483.24
11_0_2b159
For more information about JBR versions and variants, see "Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance".
JetBrains Runtime (JBR) variant to use when running the verification.
String
null
JetBrains Runtime architecture. By default, it's resolved based on the current OS and JRE architecture.
String
null
The path to the directory containing the JVM runtime. Overrides runPluginVerifier.jbrVersion.
String
null
The list of class prefixes from the external libraries. The Plugin Verifier will not report No such class for classes of these packages.
List<String>
[]
A file that contains a list of problems that will be ignored in the verification report. It must contain lines in form <plugin_xml_id>:<plugin_version>:<problem_description_regexp_pattern>.
File
null
A flag that controls the output format - if set to true, the TeamCity Tests Format (https://www.jetbrains.com/help/teamcity/service-messages.html) – the TeamCity compatible output will be returned to stdout.
Boolean
false
Specify which subsystems of the IDE should be checked.
String
all
all
android-only
without-android
Arbitrary command line arguments that are passed to the IntelliJ Plugin Verifier as is in addition to the arguments provided by the Plugin Verifier DSL.
Arguments that require a value must be provided in the separate elements of the list.
List<String>
[]
["-team-city"] as a switch
["-suppress-internal-api-usages", "jetbrains-plugins"] as an argument with a value
Setup required dependencies for building and running the project. This task is automatically added to the "After Sync" Gradle trigger (https://www.jetbrains.com/help/idea/work-with-gradle-tasks.html#config_triggers_gradle) to make the IntelliJ SDK dependency available for IntelliJ IDEA right after the Gradle synchronization.
After removing the Gradle IntelliJ Plugin from your project, the Task 'setupDependencies' not found in root project exception may occur. See Frequently Asked Questions ("Task setupDependencies not found in root project" in "Gradle IntelliJ Plugin – FAQ") for more details.
This task exposes the setupDependencies.idea property which contains a reference to the resolved IDE dependency used for building the plugin.
This property can be referred in Gradle configuration to access IDE dependency classpath.
Prepares code instrumentation tasks.
A flag that controls whether code instrumentation is enabled.
Boolean
intellij.instrumentCode
The path to the directory where instrumented classes will be saved.
Directory
${project.buildDir}/instrumented
Sign the ZIP archive with the provided key using the marketplace-zip-signer (https://github.com/JetBrains/marketplace-zip-signer) library.
To sign the plugin before publishing to JetBrains Marketplace (https://plugins.jetbrains.com) with the signPlugin task, it is required to provide a certificate chain and a private key with its password using signPlugin { ... } Plugin Signing DSL.
As soon as signPlugin.privateKey (or signPlugin.privateKeyFile) and signPlugin.certificateChain (or signPlugin.certificateChainFile) properties are specified, the task will be executed automatically right before the publishPlugin task.
For more details, see Plugin Signing (Plugin Signing) article.
A string containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert CLI option.
This property accepts value provided as a plain text or base64-encoded string.
String
null
A file containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert-file CLI option.
File
null
Encoded private key in PEM format. Refers to key CLI option.
This property accepts value provided as a plain text or base64-encoded string.
String
null
A file with the encoded private key in PEM format. Refers to key-file CLI option.
File
null
Password required to decrypt the private key. Refers to key-pass CLI option.
String
null
Returns the version of JetBrains Marketplace ZIP Signer CLI (https://github.com/JetBrains/marketplace-zip-signer) that will be used.
String
LATEST
Path to JetBrains Marketplace ZIP Signer CLI (https://github.com/JetBrains/marketplace-zip-signer) file. Takes precedence over signPlugin.cliVersion.
String
null
KeyStore file path. Refers to ks CLI option.
String
null
KeyStore password.
String
null
KeyStore key alias. Refers to ks-key-alias CLI option.
String
null
KeyStore type.
String
null
JCA KeyStore Provider name. Refers to ks-provider-name CLI option.
String
null
Input, unsigned ZIP archive file. Refers to in CLI option.
Provided by the buildPlugin task.
Output, signed ZIP archive file. Refers to out CLI option.
Predefined with the name of the ZIP archive file with -signed name suffix attached.
File
Validates completeness and contents of plugin.xml (Plugin Configuration File) descriptors as well as plugin archive structure.
Specify whether the build should fail when the verifications performed by this task fail.
Boolean
false
Specify whether the build should fail when the verifications performed by this task emit warnings.
Boolean
true
Specify whether the build should fail when the verifications performed by this task emit unacceptable warnings.
Boolean
false
The location of the built plugin file which will be used for verification.
File
${prepareSandboxTask.destinationDir}/${prepareSandboxTask.pluginName}
Validates the plugin project configuration:
The patchPluginXml.sinceBuild property can't be lower than the major version of the currently used IntelliJ SDK set with the intellij.version.
The sourceCompatibility property of the Java configuration can't be lower than the Java version used for assembling the IntelliJ SDK specified by the intellij.version.
The targetCompatibility property of the Java configuration can't be higher than the Java version required for running IDE in the version specified by the intellij.version or patchPluginXml.sinceBuild properties.
The kotlinJvmTarget property of the Kotlin configuration (if used) can't be higher than the Java version required for running IDE in the version specified by the intellij.version or patchPluginXml.sinceBuild properties.
The kotlinLanguageVersion property of the Kotlin configuration (if used) can't be lower than the Kotlin bundled with IDE in the version specified by the intellij.version or patchPluginXml.sinceBuild properties.
The kotlinApiVersion property of the Kotlin configuration (if used) can't be higher than the Kotlin bundled with IDE in the version specified by the intellij.version or patchPluginXml.sinceBuild properties.
For more details regarding the Java version used in the specific IntelliJ SDK, see Build Number Ranges.
The dependency on the Kotlin Standard Library (stdlib) is automatically added when using the Gradle Kotlin plugin and may conflict with the version provided with the IntelliJ Platform.
Read more about controlling this behavior on "Kotlin Standard Library (stdlib)" in "Configuring Kotlin Support".
An old default [runPluginVerifier.downloadDir ][#tasks-runpluginverifier-downloaddir] path contains downloaded IDEs, but another default is in use. Links to the FAQ section ("The Plugin Verifier download directory is set to [...], but downloaded IDEs were also found in [...]" in "Gradle IntelliJ Plugin – FAQ")
Validates the signature of the plugin archive file using the marketplace-zip-signer (https://github.com/JetBrains/marketplace-zip-signer) library.
For more details, see Plugin Signing (Plugin Signing) article.
Currently unavailable — please use verifyPluginSignature.certificateChainFile instead.
String
null
A file containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert CLI option.
By default, the certificate chain file is set to the value of the signPlugin.certificateChainFile property. If absent, the signPlugin.certificateChain property is used instead, but due to the CLI tool limitations, a temporary file is created and the certificate chain is written to it.
File
signPlugin.certificateChainFile
Input, signed ZIP archive file. Refers to in CLI option.
Provided by the signPlugin task.
Marketplace platform provides the IntelliJ Platform Explorer (https://jb.gg/ipe) – a search tool for browsing Extension Points inside existing implementations of open-source IntelliJ Platform plugins.
One of its features is the possibility of filtering the plugins by those that utilize Gradle (https://jb.gg/ipe?buildSystem=gradle) or Gradle KTS (https://jb.gg/ipe?buildSystem=gradle_kts) build scripts.
As examples of using this plugin, you can also check out the following projects:
Erlang plugin (https://github.com/ignatov/intellij-erlang) and its TeamCity build configuration (https://teamcity.jetbrains.com/project.html?projectId=IntellijIdeaPlugins_Erlang&tab=projectOverview)
Rust plugin (https://github.com/intellij-rust/intellij-rust)
Fully written in Kotlin
Uses Grammar-Kit (https://github.com/JetBrains/Grammar-Kit)
Perl5 plugin (https://github.com/hurricup/Perl5-IDEA)
Bamboo Soy plugin (https://github.com/google/bamboo-soy) and its Travis configuration file (https://github.com/google/bamboo-soy/blob/master/.travis.yml)
AceJump plugin (https://github.com/johnlindquist/AceJump)
EmberJS plugin (https://github.com/Turbo87/intellij-emberjs)
Robot plugin (https://github.com/AmailP/robot-plugin)
SQLDelight Android Studio Plugin (https://github.com/square/sqldelight/tree/master/sqldelight-idea-plugin)
idear plugin (https://github.com/breandan/idear)
SonarLint plugin (https://github.com/SonarSource/sonar-intellij)
IdeaVim plugin (https://github.com/JetBrains/ideavim) and its TeamCity build configuration (https://teamcity.jetbrains.com/project.html?projectId=IdeaVim&guest=1)
Adb Idea (https://github.com/pbreault/adb-idea) is configured to build and run against stable, beta or preview (canary) releases of Android Studio
Gerrit (https://github.com/uwolfer/gerrit-intellij-plugin) uses Travis CI inclusive automated publishing of releases to GitHub and JetBrains plugin repository (triggered by version tag creation)
Minecraft Development (https://github.com/minecraft-dev/MinecraftDev)
Mixes Java, Kotlin, and Groovy code
Uses Grammar-Kit (https://github.com/JetBrains/Grammar-Kit)
Uses a Kotlin version not bundled with IntelliJ IDEA
Unity 3D plugin (https://github.com/JetBrains/resharper-unity) for JetBrains Rider
AEM Tools plugin (https://github.com/aemtools/aemtools) for Adobe Experience Manager integration
Fully written in Kotlin
Uses template language
F# plugin (https://github.com/JetBrains/resharper-fsharp/tree/net222/rider-fsharp) for JetBrains Rider
Rainbow Brackets (https://github.com/izhangzhihao/intellij-rainbow-brackets)
Fully written in Kotlin
Uses other IntelliJ IDEA plugins as test dependencies
Circle CI configuration file & Travis CI configuration file
Auto submit anonymous feedback as GitHub issues
Requirements (https://github.com/meanmail-dev/requirements)
Fully written in Kotlin
Uses other IntelliJ IDEA plugins as test dependencies
Uses Grammar-Kit (https://github.com/JetBrains/Grammar-Kit)
Uses a Kotlin version not bundled with IntelliJ IDEA
EduTools (https://github.com/JetBrains/educational-plugin)
Mixes Java and Kotlin code
With ongoing Gradle IntelliJ Plugin releases, new features are introduced that require additional research, collecting more feedback from developers, or should be enabled or disabled under particular conditions. Build Features are an implementation of the feature flags concept and let you control some behaviors of the Gradle IntelliJ Plugin.
To enable or disable a particular feature, add a Project property to the gradle.properties file with the following pattern:
org.jetbrains.intellij.buildFeature.<buildFeatureName>=<true|false>E.g., to disable the selfUpdateCheck feature, add this line:
org.jetbrains.intellij.buildFeature.selfUpdateCheck=falseWhen the buildSearchableOptions ("buildSearchableOptions" in "Gradle IntelliJ Plugin") doesn't produce any results, e.g., when the plugin doesn't implement any Settings (Settings), a warning is shown to suggest disabling the task ("How to disable building searchable options?" in "Gradle IntelliJ Plugin – FAQ") for better performance.
true
org.jetbrains.intellij.buildFeature.buildSearchableOptions=falseDue to IDE limitations, it is impossible to run the IDE in headless mode to collect searchable options for a paid plugin. As paid plugins require providing a valid license and presenting a UI dialog, it is impossible to handle such a case, and the task will fail. This feature flag displays the given warning when the task is run by a paid plugin.
true
org.jetbrains.intellij.buildFeature.paidPluginSearchableOptionsWarning=falseChecks whether the currently used Gradle IntelliJ Plugin is outdated and if a new release is available. The plugin performs an update check on every run asking the GitHub Releases page for the redirection URL to the latest version with HEAD HTTP request: https://github.com/jetbrains/gradle-intellij-plugin/releases/latest.
If the current version is outdated, the plugin will emit a warning with its current and the latest version.
Feature respects the Gradle --offline (https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_execution_options) mode.
It is strongly suggested to always use the latest available version. Older plugin versions may also not fully support the latest IDE releases.
true
org.jetbrains.intellij.buildFeature.selfUpdateCheck=falseGradle: Minimum Plugin VersionsWhen targeting 2024.2 or later, IntelliJ Platform Gradle Plugin 2.x (Beta) is required.
When targeting 2022.3 or later, Gradle IntelliJ Plugin ("Usage" in "Gradle IntelliJ Plugin") version 1.10.1 or higher is required (current: 1.17.3).
runIde ("runIde" in "Gradle IntelliJ Plugin") task is a Java Exec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) task and can be modified according to the documentation.
To add some JVM arguments while launching the IDE, configure runIde ("runIde" in "Gradle IntelliJ Plugin") task as follows:
tasks {
runIde {
jvmArgs("-DmyProperty=value")
}
}runIde {
jvmArgs "-DmyProperty=value"
}Using the very same task documentation (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), configure runIde ("runIde" in "Gradle IntelliJ Plugin") task:
tasks {
runIde {
systemProperty("name", "value")
}
}runIde {
systemProperty("name", "value")
}See "Enabling Auto-Reload" in "IDE Development Instance" for important caveats.
Configure runIde ("runIde" in "Gradle IntelliJ Plugin") task as follows:
tasks {
runIde {
autoReloadPlugins.set(false)
}
}runIde {
autoReloadPlugins = false
}Building searchable options can be disabled as a task:
tasks {
buildSearchableOptions {
enabled = false
}
}buildSearchableOptions.enabled = falseAs a result of disabling building searchable options, the Settings (Settings) that your plugin provides won't be searchable in the Settings dialog. Disabling of the task is suggested for plugins that are not intended to provide custom settings.
The most convenient way to see the logs of running IDE is to add a tab to the Run tool window displaying the contents of idea.log file. In the Gradle runIde run configuration, add the log file path according to sandbox location ("The Development Instance Sandbox Directory" in "IDE Development Instance") as described in View logs (https://www.jetbrains.com/help/idea/setting-log-options.html).
prepareSandbox ("prepareSandbox" in "Gradle IntelliJ Plugin") task is a Sync (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Sync.html) task and can be modified accordingly. Something like following should work:
tasks {
prepareSandbox {
from("yourFile") {
into("${intellij.pluginName.get()}/lib/")
}
}
}prepareSandbox {
from("yourFile") {
into "${intellij.pluginName.get()}/lib/"
}
}The setupDependencies ("setupDependencies" in "Gradle IntelliJ Plugin") task is designed to fetch the target IDE dependency from the IntelliJ Repository in the after-sync Gradle phase, but only when working inside IntelliJ IDEA – to make the IntelliJ SDK classes resolved and code completion available. To achieve that, the gradle-idea-ext-plugin (https://github.com/JetBrains/gradle-idea-ext-plugin) is used, which alters the IDEA project's .idea/workspace.xml file making the setupDependencies ("setupDependencies" in "Gradle IntelliJ Plugin") task activated on after_sync event.
Unfortunately, this entry remains even after disabling the org.jetbrains.intellij plugin in a project – the setupDependencies ("setupDependencies" in "Gradle IntelliJ Plugin") task won't be resolved appropriately, which produces the following exception:
Task 'setupDependencies' not found in root project 'projectName'.To fix that, manually edit the .idea/workspace.xml file removing mentioned entry, go to the Gradle tool window, select the Tasks Activation action from the context menu of the root project item, and remove it.
See the Bundling Plugin API Sources section for details.
With the 1.10.0 release, the runPluginVerifier ("runPluginVerifier" in "Gradle IntelliJ Plugin") task uses the XDG_CACHE_HOME environment variable (see XDG Base Directory (https://wiki.archlinux.org/title/XDG_Base_Directory) for more details) to resolve the default directory for downloaded IDEs – instead of the user's home directory. We recommend moving your existing IDEs stored i.e., in ~/.pluginVerifier/ides/ directory into $XDG_CACHE_HOME/pluginVerifier/ides to avoid downloading them once again.
In case you want to keep the downloaded archives in the previous location, specify the given path explicitly to the runPluginVerifier.downloadDir ("downloadDir" in "Gradle IntelliJ Plugin") property:
tasks {
runPluginVerifier {
downloadDir.set(System.getProperty("user.home") + "/.pluginVerifier/ides")
}
}runPluginVerifier {
downloadDir = System.getProperty("user.home") + "/.pluginVerifier/ides"
}The Gradle IntelliJ Plugin, when targeting the IntelliJ SDK 2022.1+, uses the PathClassLoader class loader by the following system property:
-Djava.system.class.loader=com.intellij.util.lang.PathClassLoaderBecause of that, JaCoCo – and other external tools that rely on classes available in the bootstrap class loader – fail to discover plugin classes.
In addition, if the code instrumentation is enabled (see intellij.instrumentCode ("instrumentCode" in "Gradle IntelliJ Plugin")), it's required to switch to the compiled and instrumented output instead of a default compiled classes.
The following changes to your Gradle configuration file:
tasks {
withType<Test> {
configure<JacocoTaskExtension> {
isIncludeNoLocationClasses = true
excludes = listOf("jdk.internal.*")
}
}
jacocoTestReport {
classDirectories.setFrom(instrumentCode)
}
jacocoTestCoverageVerification {
classDirectories.setFrom(instrumentCode)
}
}test {
jacoco {
includeNoLocationClasses = true
excludes = ["jdk.internal.*"]
}
}
jacocoTestReport {
classDirectories.setFrom(instrumentCode)
}
jacocoTestCoverageVerification {
classDirectories.setFrom(instrumentCode)
}When using Java 11.0.2 for building plugins, resolving dependencies (or making any other network requests) in Gradle IntelliJ Plugin fails due to the JDK-8220723 (https://bugs.openjdk.org/browse/JDK-8220723) issue with the following exception:
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticatedTo fix that issue, upgrade the Java version to the latest patch available of the major version of your choice.
It is possible to add a dependency on a plugin available in the file system — like a plugin update downloaded manually from JetBrains Marketplace or built separately in another project.
To configure the dependency, add the File instance to the intellij.plugins ("plugins" in "Gradle IntelliJ Plugin") property and point it to the extracted plugin's directory which contains the lib directory.
intellij {
plugins.set(listOf(file("/path/to/plugin/")))
}intellij {
plugins = [file("/path/to/plugin/")]
}It is also possible to refer to the sandbox directory of another Gradle project — to do that, point to the /projects/plugin-name/build/idea-sandbox/plugins/plugin-name/ directory.
Please upgrade to Kotlin 1.9.0. See the "Incremental compilation" in "Configuring Kotlin Support" section if using Kotlin 1.8.20.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Current Release: 2.0.0-beta1
GitHub: Releases & Changelog (https://github.com/JetBrains/gradle-intellij-plugin/releases), Issue Tracker (https://github.com/JetBrains/gradle-intellij-plugin/issues)
Slack: #intellij-platform-gradle-plugin (https://jetbrains-platform.slack.com/archives/C05C80200LS) on the JetBrains Platform Slack (https://plugins.jetbrains.com/slack/)
The IntelliJ Platform Gradle Plugin 2.x is a plugin for the Gradle build system to help configure environments for building, testing, verifying, and publishing plugins for IntelliJ-based IDEs. It is a successor of Gradle IntelliJ Plugin (1.x).
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
IntelliJ Platform Gradle Plugin 2.x requires the following minimal versions:
IntelliJ Platform: 2022.3
Gradle: 8.2
See the Gradle Installation guide (https://gradle.org/install/) on how to upgrade.
Java Runtime: 17
See Gradle JVM in Settings | Build, Execution, Deployment | Build Tools | Gradle.
Please note that the plugin has a new ID org.jetbrains.intellij.platform.
To apply the IntelliJ Platform Gradle Plugin to a project, add the following entry to the plugins block in the build.gradle.kts file:
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
}If migrating from the Gradle IntelliJ Plugin 1.x, replace the old org.jetbrains.intellij identifier to org.jetbrains.intellij.platform and apply its latest 2.0.0-beta1 version.
To use the latest snapshot versions, add the following to the settings.gradle.kts file:
pluginManagement {
repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots/")
gradlePluginPortal()
}
}The snapshot release is published with the constant version, creating a possibility for Gradle to resort to the cached version of the plugin. To update all dependencies in the dependency cache, use the --refresh-dependencies command line option.
The IntelliJ Platform Gradle Plugin consists of multiple plugins (Plugins) which can be applied in bundles ("Platform" in "Plugins" or "Module" in "Plugins") or separately.
Subplugins architecture allows applying a subset of features, e.g., to provide the IntelliJ Platform dependency to a project submodule without creating unnecessary tasks.
Exploring Configuration OptionsAuto-completion, Quick Documentation, and other code insight features are available for many extension functions and properties.
All IntelliJ Platform SDK artifacts are available via IntelliJ Maven repositories (see IntelliJ Platform Artifacts Repositories), which exist in three variants:
releases
snapshots
nightly (only selected artifacts)
In most cases, defaultRepositories() ("Default Repositories" in "Repositories Extension") repository should be sufficient.
Example:
Setup Maven Central and defaultRepositories() ("Default Repositories" in "Repositories Extension") repositories:
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}Example #2:
Build a plugin against a release version of the IntelliJ Platform with dependency on a plugin from the JetBrains Marketplace:
repositories {
mavenCentral()
intellijPlatform {
releases()
marketplace()
}
}See Repositories Extension on how to configure additional repositories.
To access the IntelliJ Platform Gradle Plugin within the settings.gradle.kts to use with dependencyResolutionManagement, add:
import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform
plugins {
id("org.jetbrains.intellij.platform.settings") version "2.0.0-beta1"
}
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
}Some repositories, by default, point to JetBrains Cache Redirector to provide better resource resolution. However, it is possible to use the direct repository URL, if available.
To switch off the default usage of JetBrains Cache Redirector, see the "useCacheRedirector" in "Gradle Properties" build feature.
Dependencies and repositories are handled using explicit entries within dependencies {} and repositories {} blocks in build.gradle.kts file.
A minimum configuration for targeting IntelliJ IDEA Community 2023.3:
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2023.3")
}
}The intellijIdeaCommunity in the previous sample is one of the extension functions available for adding IntelliJ Platform dependencies to the project. See Dependencies Extension on how to target other IDEs.
As a fallback, intellijPlatform extension can be used to allow dynamic configuration of the target platform, e.g., via gradle.properties:
platformType = IC
platformVersion = 2023.3The above Gradle properties can be referenced in the build.gradle.kts file with:
dependencies {
intellijPlatform {
val type = providers.gradleProperty("platformType")
val version = providers.gradleProperty("platformVersion")
create(type, version)
}
}The intellijPlatform helper accepts also the IntelliJPlatformType ("IntelliJPlatformType" in "Types") type:
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
dependencies {
intellijPlatform {
val version = providers.gradleProperty("platformVersion")
create(IntelliJPlatformType.IntellijIdeaUltimate, version)
}
}It is possible to refer to the locally available IntelliJ-based IDE using the local helper function:
repositories {
intellijPlatform {
localPlatformArtifacts()
}
}
dependencies {
intellijPlatform {
local("/Users/user/Applications/IntelliJ IDEA Ultimate.app")
}
}localPlatformArtifacts() and defaultRepositories()Note that unless using recommended default defaultRepositories() ("Default Repositories" in "Repositories Extension"), the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry needs to be added to the repositories {} block explicitly to use local dependencies (bundled plugins, local IDE, etc.).
To specify a dependency on a plugin, it is important to distinguish bundled plugins from plugins available in JetBrains Marketplace.
The Dependencies Extension provides a set of helpers to manage plugin dependencies ("Plugins" in "Dependencies Extension"):
repositories {
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2024.1")
bundledPlugin("com.intellij.java")
plugin("org.intellij.scala", "2024.1.4")
}
}localPlatformArtifacts() and defaultRepositories()Note that unless using recommended default defaultRepositories() ("Default Repositories" in "Repositories Extension"), the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry needs to be added to the repositories {} block explicitly to use local dependencies (bundled plugins, local IDE, etc.).
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
The IntelliJ Platform Gradle Plugin consists of multiple subplugins which can be applied in bundles ( or ) or separately.
Subplugins architecture allows applying a subset of features, e.g., to provide the IntelliJ Platform dependency to a project submodule without creating unnecessary tasks.
Plugins depend on each other.
When applying the plugin, there is no need for manual applying the plugin.
The following chart describes dependencies between plugins provided with the IntelliJ Platform Gradle Plugin.
The plugins highlighted in bold are recommended for most of the cases when creating a plugin for IntelliJ-based IDEs.
Plugin ID: org.jetbrains.intellij.platform
This is a top-level plugin that applies all project-level subplugins that bring the fully flagged tooling for plugin development for IntelliJ-based IDEs.
This plugin should be used in most cases when working with a single-module project:
build.gradle.kts
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
}Included plugins:
Plugin ID: org.jetbrains.intellij.platform.module
This top-level plugin applies a smaller set of subplugins required for providing required dependencies and build/test tasks for a submodule when working on a plugin for IntelliJ-based IDEs in a multi-module architecture.
Comparing to the main plugin, it doesn't contain tasks related to publishing or running the IDE for testing purposes.
settings.gradle.kts
rootProject.name = "..."
include(":submodule")build.gradle.kts
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
}
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
implementation(project(":submodule"))
intellijPlatform {
intellijIdeaCommunity("2024.1")
}
}submodule/build.gradle.kts
plugins {
id("org.jetbrains.intellij.platform.module")
}
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2024.1")
}
}Included plugins:
Plugin ID: org.jetbrains.intellij.platform.settings
If repositories are defined within the settings.gradle.kts using the dependencyResolutionManagement Gradle, make sure to include the Settings plugin in settings.gradle.kts.
See "Dependency Resolution Management" in "IntelliJ Platform Gradle Plugin 2.x (Beta)" for more details.
settings.gradle.kts
import org.jetbrains.intellij.platform.gradle.extensions.intellijPlatform
plugins {
id("org.jetbrains.intellij.platform.settings") version "2.0.0-beta1"
}
rootProject.name = "..."
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
}
include(":submodule")build.gradle.kts
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
}
dependencies {
implementation(project(":submodule"))
intellijPlatform {
intellijIdeaCommunity("2024.1")
}
}submodule/build.gradle.kts
plugins {
id("org.jetbrains.intellij.platform.module")
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2024.1")
}
}Plugin ID: org.jetbrains.intellij.platform.migration
The Migration plugin is designed to assist in upgrading projects using Gradle IntelliJ Plugin 1.x. To prevent Gradle failing due to breaking changes, the org.jetbrains.intellij.platform.migration plugin was introduced to fill missing gaps and provide migration hints.
It loads the plugin with additional mocks and checks applied — after the successful migration, it is recommended to replace the org.jetbrains.intellij.platform.migration identifier with org.jetbrains.intellij.platform.
See Migrating from Gradle IntelliJ Plugin for more details.
Plugin ID: org.jetbrains.intellij.platform.base
Sets up all the custom configurations and transformers needed to manage the IntelliJ Platform dependency, JetBrains Runtime, CLI tools, and other plugins when they're added as dependencies.
It also introduces the IntelliJ Platform Extension to the build.gradle.kts file along with Dependencies Extension and Repositories Extension to help preconfiguring project dependencies:
repositories {
...
intellijPlatform {
// Repositories Extension
}
}
dependencies {
...
intellijPlatform {
// Dependencies Extension
}
}
intellijPlatform {
// IntelliJ Platform Extension
}Plugin also introduces a task listener which allows for creating custom tasks decorated with Task Awares.
Included tasks:
Plugin ID: org.jetbrains.intellij.platform.build
Registers and preconfigures tasks responsible for patching, instrumenting, and building the plugin.
Included tasks:
Plugin ID: org.jetbrains.intellij.platform.publish
Adds tasks responsible for signing and publishing the final plugin archive to JetBrains Marketplace.
Included tasks:
Plugin ID: org.jetbrains.intellij.platform.run
Registers the task used for running the local instance of the IntelliJ Platform used for development.
It allows introducing custom tasks, so it is possible to run a plugin against various IDEs during the development process.
Included tasks:
Plugin ID: org.jetbrains.intellij.platform.test
Preconfigures the existing test task to make the plugin testing possible (unit tests, UI tests, performance tests). In addition, it preconfigures the customizable TestIdeTask class, so it is possible to register multiple test* tasks for running tests against different IDEs.
Included tasks:
Plugin ID: org.jetbrains.intellij.platform.verify
Introduces various verification tasks that run checks against project configuration, plugin.xml file, signature check, or execute the IntelliJ Plugin Verifier tool.
Included tasks:
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
The IntelliJ Platform Gradle Plugin introduces a top-level intellijPlatform extension. It consists of sections dedicated to the general Gradle plugin configuration, plugin.xml definition, publishing, signing, and verifying of the output plugin for IntelliJ-based IDEs.
After the IntelliJ Platform Gradle Plugin is applied ("Usage" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"), the intellijPlatform extension can be used to configure the plugin and common settings of the provided tasks.
Example:
intellijPlatform {
buildSearchableOptions = true
instrumentCode = true
projectName = project.name
sandboxContainer = "..."
pluginConfiguration {
// ...
}
publishing {
// ...
}
signing {
// ...
}
verifyPlugin {
// ...
}
}Provides read-only access to the IntelliJ Platform project cache location.
The IntelliJ Platform cache is used for storing IntelliJ Platform Gradle Plugin-specific files, such as:
XML files generated for the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") local Ivy repository
coroutines Java agent file created by the initializeIntelliJPlatformPlugin ("initializeIntelliJPlatformPlugin" in "Tasks") task
This path can be changed with the org.jetbrains.intellij.platform.intellijPlatformCache ("intellijPlatformCache" in "Gradle Properties") Gradle property
Read-only
Path
[rootProject]/.intellijPlatform/
Provides read-only access to the IntelliJ Platform dependency artifact path.
Read-only
Path
Path of the current IntelliJ Platform
Provides read-only access to the ProductInfo ("ProductInfo" in "Types") object associated with the IntelliJ Platform dependency configured for the current project.
Read-only
ProductInfo ("ProductInfo" in "Types")
ProductInfo ("ProductInfo" in "Types") of the current IntelliJ Platform
Enables auto-reload of dynamic plugins. Dynamic plugin will be reloaded automatically when its content is modified.
This allows a much faster development cycle by avoiding a full restart of the development instance after code changes.
Property<Boolean>
true
See also:
Task Awares: AutoReloadAware ("AutoReloadAware" in "Task Awares")
Builds an index of UI components (searchable options) for the plugin. Controls the execution of the buildSearchableOptions ("buildSearchableOptions" in "Tasks") task.
Property<Boolean>
true
See also:
Build Features: noSearchableOptionsWarning ("noSearchableOptionsWarning" in "Gradle Properties")
Enables the "instrumentCode" in "Tasks" of the compiled classes.
Controls the execution of the instrumentCode ("instrumentCode" in "Tasks") task.
Property<Boolean>
true
Defines the project name, which is used for creating file structure and the build archive.
Property<String>
project.name
The path to the sandbox container where tests and IDE instances read and write data.
DirectoryProperty
[buildDirectory]/idea-sandbox
See also:
Tasks: prepareSandbox ("prepareSandbox" in "Tasks")
Task Awares: SandboxAware ("SandboxAware" in "Task Awares")
Split Mode requires the IntelliJ Platform in version 241.14473 or later.
Allows for checking how a plugin works in remote development mode, when one machine is running the backend part and another is running a frontend part (JetBrains Client) which connects to the backend.
This property allows running the IDE with backend and frontend parts running in separate processes. The developed plugin is installed in the backend part.
Property<Boolean>
true
See also:
Task Awares: SplitModeAware ("SplitModeAware" in "Task Awares")
Configures the plugin definition and stores values in the plugin.xml file. Data provided to the intellijPlatform.pluginConfiguration {} extension is passed to the patchPluginXml ("patchPluginXml" in "Tasks") task, which augments the plugin.xml file with new values.
Requires the "Build" in "Plugins" plugin to be applied.
Example:
intellijPlatform {
// ...
pluginConfiguration {
id = "my-plugin-id"
name = "My Awesome Plugin"
version = "1.0.0"
description = "It's an awesome plugin!"
changeNotes =
"""
A descriptive release note...
""".trimIndent()
productDescriptor {
// ...
}
ideaVersion {
// ...
}
vendor {
// ...
}
}
}See also:
The plugin's unique identifier. This should mirror the structure of fully qualified Java packages and must remain distinct from the IDs of existing plugins. This ID is a technical descriptor used not only within the IDE, but also on JetBrains Marketplace (https://plugins.jetbrains.com/).
Please restrict input to characters, numbers, and ./-/_ symbols, and aim for a concise length.
The provided value will populate the <id> ("id" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.pluginId ("pluginId" in "Tasks")
The plugin's display name, visible to users. It should use Title Case.
The provided value is used to populate the <name> ("name" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.pluginName ("pluginName" in "Tasks")
The plugin version, presented in the Plugins settings dialog and on its JetBrains Marketplace page.
For plugins uploaded to the JetBrains Marketplace, semantic versioning (https://plugins.jetbrains.com/docs/marketplace/semver.html) must be adhered to.
The provided value is used as a <version> ("version" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.pluginVersion ("pluginVersion" in "Tasks")
The plugin description, which is presented on the JetBrains Marketplace plugin page and in the Plugins settings dialog. Basic HTML elements such as text formatting, paragraphs, and lists are permitted.
The description content is automatically enclosed in <![CDATA[... ]]>.
The provided value is used to populate the <description> ("description" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.pluginDescription ("pluginDescription" in "Tasks")
A concise summary of new features, bug fixes, and alterations provided in the latest plugin version. These change notes will be displayed on the JetBrains Marketplace plugin page and in the Plugins settings dialog. Basic HTML elements such as text formatting, paragraphs, and lists are permitted.
The change notes content is automatically enclosed in <![CDATA[... ]]>.
The provided value is used to populate the <change-notes> ("change-notes" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.changeNotes ("changeNotes" in "Tasks")
A part of the which describes the product-descriptor element.
Example:
intellijPlatform {
// ...
pluginConfiguration {
// ...
productDescriptor {
code = "MY_CODE"
releaseDate = "20240217"
releaseVersion = "20241"
optional = false
}
}
}See also:
How to add required parameters for paid plugins (https://plugins.jetbrains.com/docs/marketplace/add-required-parameters.html)
The product code for the plugin, used in the JetBrains Sales System. The value must be pre-approved by JetBrains and must adhere to specified requirements (https://plugins.jetbrains.com/docs/marketplace/obtain-a-product-code-from-jetbrains.html).
The provided value is used for a <product-descriptor code=""> element attribute.
Property<String>
See also:
Tasks: patchPluginXml.productDescriptorCode ("productDescriptorCode" in "Tasks")
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
The release date of the major version, formatted as YYYYMMDD.
The provided value is used for the <product-descriptor release-date=""> element attribute.
Property<String>
See also:
Tasks: patchPluginXml.productDescriptorReleaseDate ("productDescriptorReleaseDate" in "Tasks")
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
The major version, represented in a specific numerical format.
The provided value is used for the <product-descriptor release-version=""> element attribute.
Property<String>
See also:
Tasks: patchPluginXml.productDescriptorReleaseVersion ("productDescriptorReleaseVersion" in "Tasks")
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
The boolean value that indicates if the plugin is a Freemium (https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin.
The provided value is used for the <product-descriptor optional=""> element attribute.
Property<Boolean>
false
See also:
Tasks: patchPluginXml.productDescriptorOptional ("productDescriptorOptional" in "Tasks")
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
A part of the which describes the <idea-version> ("idea-version" in "Plugin Configuration File") element.
Example:
intellijPlatform {
// ...
pluginConfiguration {
// ...
ideaVersion {
sinceBuild = "241"
untilBuild = "241.*"
}
}
}See also:
The earliest IDE version that is compatible with the plugin.
The provided value is used for the <idea-version since-build=""/> element attribute.
The default value is set to the MAJOR.MINOR version based on the currently selected IntelliJ Platform, like 233.12345.
Property<String>
MAJOR.MINOR
See also:
Tasks: patchPluginXml.sinceBuild ("sinceBuild" in "Tasks")
The latest IDE version that is compatible with the plugin. An undefined value signifies compatibility with all IDEs starting from the version mentioned in since-build, including potential future builds that may cause compatibility issues.
The provided value is used for the <idea-version until-build=""/> element attribute.
The default value is set to the MAJOR.* version based on the currently selected IntelliJ Platform, such as 233.*.
The until-build attribute can be unset by setting provider { null } as a value. Note that passing only null will make Gradle use a default value instead.
Property<String>
MAJOR.*
See also:
Tasks: patchPluginXml.untilBuild ("untilBuild" in "Tasks")
A part of the which describes the <vendor> ("vendor" in "Plugin Configuration File") element.
Example:
intellijPlatform {
// ...
pluginConfiguration {
// ...
vendor {
name = "JetBrains"
email = "hello@jetbrains.com"
url = "https://www.jetbrains.com"
}
}
}The name of the vendor or the organization ID (if created), as displayed in the Plugins settings dialog and on the JetBrains Marketplace plugin page.
The provided value is used as the value of the <vendor> ("vendor" in "Plugin Configuration File") element.
Property<String>
See also:
Tasks: patchPluginXml.vendorName ("vendorName" in "Tasks")
The email address of the vendor.
The provided value is used for the <vendor email=""> element attribute.
Property<String>
See also:
Tasks: patchPluginXml.vendorEmail ("vendorEmail" in "Tasks")
The URL to the vendor's homepage.
The provided value is used for the <vendor url=""> element attribute.
Property<String>
See also:
Tasks: patchPluginXml.vendorUrl ("vendorUrl" in "Tasks")
Configures the publishing process of the plugin. All values are passed to the "publishPlugin" in "Tasks" task.
Requires the "Publish" in "Plugins" plugin to be applied.
Example:
intellijPlatform {
// ...
publishing {
host = ""
token = "7hR4nD0mT0k3n_8f2eG"
channels = listOf("default")
ideServices = false
hidden = false
}
}The hostname used for publishing the plugin.
Property<String>
https://plugins.jetbrains.com
See also:
Tasks: publishPlugin.host ("host" in "Tasks")
Authorization token.
Property<String>
yes
See also:
Tasks: publishPlugin.token ("token" in "Tasks")
A list of channel names to upload plugin to.
ListProperty<String>
listOf("default")
See also:
Tasks: publishPlugin.channels ("channels" in "Tasks")
Specify if the IDE Services plugin repository service should be used.
Property<String>
false
See also:
Tasks: publishPlugin.ideServices ("ideServices" in "Tasks")
Publish the plugin update and mark it as hidden to prevent public visibility after approval.
Property<String>
false
See also:
Tasks: publishPlugin.hidden ("hidden" in "Tasks")
Hidden release (https://plugins.jetbrains.com/docs/marketplace/hidden-plugin.html)
Plugin signing configuration.
Requires the "Publish" in "Plugins" plugin to be applied.
Example:
intellijPlatform {
// ...
signing {
cliPath = file("/path/to/marketplace-zip-signer-cli.jar")
keyStore = file("/path/to/keyStore.ks")
keyStorePassword = "..."
keyStoreKeyAlias = "..."
keyStoreType = "..."
keyStoreProviderName = "..."
privateKey = "..."
privateKeyFile = file("/path/to/private.pem")
password = "..."
certificateChain = "..."
certificateChainFile = file("/path/to/chain.crt")
}
}See also:
Tasks: signPlugin ("signPlugin" in "Tasks")
Task Awares: SigningAware ("SigningAware" in "Task Awares")
Marketplace ZIP Signer (https://github.com/JetBrains/marketplace-zip-signer)
A path to the local Marketplace ZIP Signer CLI tool to be used.
RegularFileProperty
See also:
Task Awares: SigningAware ("SigningAware" in "Task Awares")
KeyStore file path. Refers to ks CLI option.
Property<String>
See also:
Tasks: signPlugin.keyStore ("keyStore" in "Tasks")
KeyStore password. Refers to ks-pass CLI option.
Property<String>
See also:
Tasks: signPlugin.keyStorePassword ("keyStorePassword" in "Tasks")
KeyStore key alias. Refers to ks-key-alias CLI option.
Property<String>
See also:
Tasks: signPlugin.keyStoreKeyAlias ("keyStoreKeyAlias" in "Tasks")
KeyStore type. Refers to ks-type CLI option.
Property<String>
See also:
Tasks: signPlugin.keyStoreType ("keyStoreType" in "Tasks")
JCA KeyStore Provider name. Refers to ks-provider-name CLI option.
Property<String>
See also:
Tasks: signPlugin.keyStoreProviderName ("keyStoreProviderName" in "Tasks")
Encoded private key in the PEM format. Refers to key CLI option.
Takes precedence over the property.
Property<String>
See also:
Tasks: signPlugin.privateKey ("privateKey" in "Tasks")
A file with an encoded private key in the PEM format. Refers to key-file CLI option.
RegularFileProperty
See also:
Tasks: signPlugin.privateKeyFile ("privateKeyFile" in "Tasks")
Password required to decrypt the private key. Refers to key-pass CLI option.
Property<String>
See also:
Tasks: signPlugin.password ("password" in "Tasks")
A string containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert CLI option.
Takes precedence over the property.
Property<String>
See also:
Tasks: signPlugin.certificateChain ("certificateChain" in "Tasks")
Path to the file containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert-file CLI option.
RegularFileProperty
See also:
Tasks: signPlugin.certificateChainFile ("certificateChainFile" in "Tasks")
IntelliJ Plugin Verifier CLI tool configuration.
Requires the "Verify" in "Plugins" plugin to be applied.
Example:
intellijPlatform {
// ...
verifyPlugin {
cliPath = file("/path/to/plugin-verifier-cli.jar")
freeArgs = listOf("foo", "bar")
homeDirectory = file("/path/to/pluginVerifierHomeDirectory/")
downloadDirectory = file("/path/to/pluginVerifierHomeDirectory/ides/")
failureLevel = VerifyPluginTask.FailureLevel.ALL
verificationReportsDirectory = "build/reports/pluginVerifier"
verificationReportsFormats = VerifyPluginTask.VerificationReportsFormats.ALL
externalPrefixes = "com.example"
teamCityOutputFormat = false
subsystemsToCheck = VerifyPluginTask.Subsystems.ALL
ignoredProblemsFile = file("/path/to/ignoredProblems.txt")
ides {
// ...
}
}
}See also:
Tasks: verifyPlugin ("verifyPlugin" in "Tasks")
Task Awares: PluginVerifierAware ("PluginVerifierAware" in "Task Awares")
IntelliJ Plugin Verifier CLI (https://github.com/JetBrains/intellij-plugin-verifier)
A path to the local IntelliJ Plugin Verifier CLI tool to be used.
RegularFileProperty
See also:
Task Awares: PluginVerifierAware ("PluginVerifierAware" in "Task Awares")
The path to the directory where IDEs used for the verification will be downloaded.
DirectoryProperty
homeDirectory/ides
Defines the verification level at which the task should fail if any reported issue matches.
ListProperty<FailureLevel>
FailureLevel.COMPATIBILITY_PROBLEMS ("FailureLevel" in "Types")
See also:
Tasks: verifyPlugin.failureLevel ("failureLevel" in "Tasks")
The list of class prefixes from the external libraries. The Plugin Verifier will not report No such class for classes of these packages.
ListProperty<String>
See also:
Tasks: verifyPlugin.externalPrefixes ("externalPrefixes" in "Tasks")
The list of free arguments is passed directly to the IntelliJ Plugin Verifier CLI tool.
They can be used in addition to the arguments that are provided by dedicated options.
ListProperty<String>
See also:
Tasks: verifyPlugin.freeArgs ("freeArgs" in "Tasks")
Retrieve the Plugin Verifier home directory used for storing downloaded IDEs. Following home directory resolving method is taken directly from the Plugin Verifier to keep the compatibility.
DirectoryProperty
Directory specified with plugin.verifier.home.dir system property
Directory specified with XDG_CACHE_HOME environment variable
~/.cache/pluginVerifier
[buildDirectory]/tmp/pluginVerifier
A file that contains a list of problems that will be ignored in a report.
RegularFileProperty
See also:
Tasks: verifyPlugin.ignoredProblemsFile ("ignoredProblemsFile" in "Tasks")
Which subsystems of the IDE should be checked.
Subsystems
Subsystems.ALL ("Subsystems" in "Types")
See also:
Tasks: verifyPlugin.subsystemsToCheck ("subsystemsToCheck" in "Tasks")
A flag that controls the output format. If set to true, the TeamCity (https://www.jetbrains.com/teamcity/) compatible output will be returned to stdout.
Property<Boolean>
false
See also:
Tasks: verifyPlugin.teamCityOutputFormat ("teamCityOutputFormat" in "Tasks")
The path to the directory where verification reports will be saved.
DirectoryProperty
[buildDirectory]/reports/pluginVerifier
See also:
Tasks: verifyPlugin.verificationReportsDirectory ("verificationReportsDirectory" in "Tasks")
The output formats of the verification reports.
ListProperty<VerificationReportsFormats>
VerificationReportsFormats.PLAIN ("VerificationReportsFormats" in "Types"), FailureVerificationReportsFormats ("VerificationReportsFormats" in "Types")
See also:
Tasks: verifyPlugin.verificationReportsFormats ("verificationReportsFormats" in "Tasks")
The extension to define the IDEs to be used along with the IntelliJ Plugin Verifier CLI tool for the binary plugin verification.
It provides a set of helpers which add relevant entries to the configuration, which later is used to resolve IntelliJ-based IDE binary releases.
Example:
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
intellijPlatform {
// ...
verifyPlugin {
// ...
ides {
ide(IntelliJPlatformType.PhpStorm)
ide(IntelliJPlatformType.RustRover, "2023.3")
local(file("/path/to/ide/"))
recommended()
select {
types = listOf(IntelliJPlatformType.PhpStorm)
channels = listOf(ProductRelease.Channel.RELEASE)
sinceBuild = "232"
untilBuild = "241.*"
}
}
}
}See also:
Tasks: verifyPlugin ("verifyPlugin" in "Tasks")
Types: IntelliJPlatformType ("IntelliJPlatformType" in "Types")
Types: ProductRelease.Channel ("ProductRelease.Channel" in "Types")
Types: ProductReleasesValueSource.FilterParameters ("ProductReleasesValueSource.FilterParameters" in "Types")
Function | Description |
|---|---|
ide(type, version) ide(definition) | Adds a dependency to a binary IDE release to be used for testing with the IntelliJ Plugin Verifier. |
local(localPath) | Adds the local IDE to be used for testing with the IntelliJ Plugin Verifier. |
recommended() | Retrieves matching IDEs using the default configuration based on the currently used IntelliJ Platform and applies them for IntelliJ Platform Verifier using the ide helper method. |
select(configure) | Retrieves matching IDEs using custom ProductReleasesValueSource.FilterParameters ("ProductReleasesValueSource.FilterParameters" in "Types") filter parameters. |
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
This is an extension class for managing IntelliJ Platform repositories in a Gradle build script. It's applied to the RepositoryHandler.
Available in both Project scope and Gradle Settings for DependencyResolutionManagement.
It provides methods to add:
IntelliJ Platform repositories (for releases, snapshots, and nightly builds)
JetBrains Marketplace repository (for dependencies on non-bundled plugins)
JetBrains Runtime repository
Android Studio and IntelliJ Platform binary release repositories (for IntelliJ Plugin Verifier)
Ivy local repository (for access to local dependencies)
In most cases, defaultRepositories() ("Default Repositories" in "Repositories Extension") repository should be sufficient.
Example:
Setup Maven Central and defaultRepositories() ("Default Repositories" in "Repositories Extension") repositories:
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}The default repository definition suitable for most plugins.
Function | Description |
|---|---|
defaultRepositories() | Applies a set of recommended repositories required for building plugins and running the most common tasks. |
It includes:
releases() and snapshots() — IntelliJ Platform releases channels
marketplace() — JetBrains Marketplace plugins repository
localPlatformArtifacts() — required to use plugins bundled with IntelliJ Platform or refer to a local IDE
intellijDependencies() — required for resolving extra IntelliJ Platform dependencies used for running specific tasks
binaryReleases() — JetBrains IDEs releases required for running the IntelliJ Plugin Verifier
Function | Description |
|---|---|
releases() | Adds a repository for accessing IntelliJ Platform stable releases. |
snapshots() | Adds a repository for accessing IntelliJ Platform snapshot releases. |
nightly() | Adds a repository for accessing IntelliJ Platform nightly releases, not available publicly. |
See also:
Function | Description |
|---|---|
binaryReleases() | Adds a repository for accessing IntelliJ Platform IDE binary releases for use with IntelliJ Plugin Verifier. |
binaryReleasesAndroidStudio() | Adds a repository for accessing Android Studio binary releases for use with IntelliJ Plugin Verifier. |
See also:
Function | Description |
|---|---|
localPlatformArtifacts() | Certain dependencies, such as the local IntelliJ Platform instance ("Local IntelliJ Platform IDE Instance" in "IntelliJ Platform Gradle Plugin 2.x (Beta)") and bundled IDE plugins, need extra pre-processing before they can be correctly used by the IntelliJ Platform Gradle Plugin and loaded by Gradle. This pre-processing involves generating XML files that detail these specific artifacts. Once created, these are stored in a unique custom Ivy (https://ant.apache.org/ivy/) repository directory. |
intellijDependencies() | Adds a repository for accessing IntelliJ Platform dependencies. |
jetbrainsRuntime() | Adds a repository for accessing JetBrains Runtime ("Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance") releases. |
marketplace() | Adds a repository for accessing plugins hosted on JetBrains Marketplace (https://plugins.jetbrains.com). |
See also:
localPlatformArtifacts() and defaultRepositories()Note that unless using recommended default defaultRepositories() ("Default Repositories" in "Repositories Extension"), the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry needs to be added to the repositories {} block explicitly to use local dependencies (bundled plugins, local IDE, etc.).
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
IntelliJ Platform Gradle Plugin enhances the dependencies {} configuration block by applying a nested dependencies.intellijPlatform {} extension.
This class provides methods for adding dependencies to different IntelliJ Platform products and managing local dependencies.
It also includes methods for adding plugins (including bundled), JetBrains Runtime, as well as tools like IntelliJ Plugin Verifier and Marketplace ZIP Signer.
Corresponding required repositories must be defined in the repositories {} section, see Repositories Extension.
Example:
setup Maven Central and defaultRepositories() ("Default Repositories" in "Repositories Extension")
target IntelliJ IDEA Community 2024.1
add dependency on the bundled Java plugin
add IntelliJ Plugin Verifier, Marketplace ZIP Signer CLI, and code instrumentation tools
add Test Framework for testing plugin with JUnit4
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2024.1")
bundledPlugin("com.intellij.java")
pluginVerifier()
zipSigner()
instrumentationTools()
testFramework(TestFrameworkType.Platform.JUnit4)
}
// other dependencies, e.g., 3rd-party libraries
}Only one IntelliJ Platform dependency can be added to the project at a time.
See for non-default targets.
Function | Description |
|---|---|
androidStudio(version) | Android Studio (Android Studio Plugin Development) |
aqua(version) | |
clion(version) | CLion (CLion Plugin Development) |
datagrip(version) | DataGrip (DataGrip Plugin Development) |
dataspell(version) | DataSpell (https://www.jetbrains.com/dataspell/) |
fleetBackend(version) | Fleet Backend |
gateway(version) | Gateway |
goland(version) | GoLand (GoLand Plugin Development) |
intellijIdeaCommunity(version) | IntelliJ IDEA Community (IntelliJ IDEA Plugin Development) |
intellijIdeaUltimate(version) | IntelliJ IDEA Ultimate (IntelliJ IDEA Ultimate) |
mps(version) | |
phpstorm(version) | PhpStorm (PhpStorm Plugin Development) |
pycharmCommunity(version) | PyCharm Community (PyCharm Plugin Development) |
pycharmProfessional(version) | PyCharm Professional (PyCharm Plugin Development) |
rider(version) | Rider (Rider Plugin Development) |
rubymine(version) | RubyMine (RubyMine Plugin Development) |
rustrover(version) | RustRover (https://www.jetbrains.com/rust/) |
webstorm(version) | WebStorm (WebStorm Plugin Development) |
writerside(version) | Writerside (https://www.jetbrains.com/writerside/) |
Function | Description |
|---|---|
create(type, version) | Adds a configurable dependency on the IntelliJ Platform. See "Parametrize IntelliJ Platform Dependency" in "IntelliJ Platform Gradle Plugin 2.x (Beta)". |
local(localPath) | Adds a dependency on a local IntelliJ Platform instance. See "Local IntelliJ Platform IDE Instance" in "IntelliJ Platform Gradle Plugin 2.x (Beta)". |
See also:
Types: IntelliJPlatformType ("IntelliJPlatformType" in "Types")
Bundled vs. Non-Bundled PluginsUse the correct function depending on whether the targeted plugin is bundled with the Target Platform or not.
Function | Description |
|---|---|
bundledPlugin(id) | Adds a dependency on a bundled IntelliJ Platform plugin. |
bundledPlugins(ids) | Adds dependencies on bundled IntelliJ Platform plugins. |
plugin(id, version, channel) | Adds a dependency on a plugin for IntelliJ Platform. |
plugin(notation) | Adds a dependency on a plugin for IntelliJ Platform using a string notation: pluginId:version or pluginId:version@channel |
plugins(notations) | Adds dependencies on plugins for IntelliJ Platform using a string notation: pluginId:version or pluginId:version@channel |
See also:
To implement tests (Testing Overview) for IntelliJ Platform plugin, it is necessary to explicitly add a dependency on the test-framework library containing the necessary test base classes. In most cases, the Platform.JUnit4 variant will be used:
import org.jetbrains.intellij.platform.gradle.TestFrameworkType
dependencies {
intellijPlatform {
testFramework(TestFrameworkType.Platform.JUnit4)
}
}The provided testFramework(type, version) helper method makes it possible to add the base artifact to the test classpath or its variants, such as Java, Go, ReSharper, etc.
Function | Description |
|---|---|
testFramework(type, version) | Adds a dependency on Test Framework or its variant using TestFrameworkType ("TestFrameworkType" in "Types") type. |
In rare cases, when the presence of a bundled $PLATFORM_PATH$/lib/testFramework.jar library is necessary (like in the case of Rider, as its test-framework is not published as an artifact), it is possible to attach it by using the TestFrameworkType.Platform.Bundled ("TestFrameworkType" in "Types") type.
See also:
Types: TestFrameworkType ("TestFrameworkType" in "Types")
Function | Description |
|---|---|
pluginVerifier(version) | Adds a dependency on IntelliJ Plugin Verifier (Verifying Plugin Compatibility). |
zipSigner(version) | Adds a dependency on Marketplace ZIP Signer (Plugin Signing). |
bundledLibrary(path) | SEE NOTE BELOW Adds a dependency on a bundled library JAR file of the current IntelliJ Platform, like lib/annotations.jar |
Do not use bundledLibrary() in production, as direct access to the IntelliJ Platform libraries is not recommended.
It should only be used as a workaround in case the IntelliJ Platform Gradle Plugin is not aligned with the latest IntelliJ Platform classpath changes.
See also:
Verifying Plugin Compatibility, Tasks: verifyPlugin ("verifyPlugin" in "Tasks")
Plugin Signing, Tasks: signPlugin ("signPlugin" in "Tasks")
Using the jetbrainsRuntime() dependency helper, it is possible to load a custom version of JetBrains Runtime. However, it is recommended to rely on the runtime bundled within the IntelliJ Platform dependency, if present.
Function | Description |
|---|---|
jetbrainsRuntime(version, variant, architecture) jetbrainsRuntime(explicitVersion) | Adds a dependency on JetBrains Runtime ("Using a JetBrains Runtime for the Development Instance" in "IDE Development Instance"). |
See the JetBrains Runtime releases page (https://github.com/JetBrains/JetBrainsRuntime/releases) for the list of available releases.
The code instrumentation process handled with the instrumentCode ("instrumentCode" in "Tasks") task, requires extra dependencies to work and properly adjust the Java bytecode. There's the instrumentationTools() dependencies helper introduced to apply all required dependencies using default configuration, however, it is possible to add and configure them separately.
Adds a Java Compiler dependency for code instrumentation. The version is determined by the IntelliJ Platform build number. If the exact version is unavailable, the closest one is used, found by scanning all releases.
Function | Description |
|---|---|
instrumentationTools() | A helper function to apply all required dependencies: javaCompiler() |
javaCompiler() javaCompiler(version) | Adds a dependency on Java Compiler. |
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
The IntelliJ Platform Gradle Plugin introduces a set of tasks to handle activities of the plugin development for IntelliJ-based IDEs, such as building, verifying, testing, and publishing the plugin archive.
Tasks are applied to the project with Plugins depending on their contect.
Each of the tasks has relations described between each other, inherit from Task Awares interfaces, respect configuration and build cache, and can be configured independently, but for the most cases, the IntelliJ Platform Extension covers all necessary cases.
Depends on: jarSearchableOptions, prepareSandbox
Extends: Zip (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html)
Sources: BuildPluginTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/BuildPluginTask.kt)
Builds the plugin and prepares the ZIP archive for testing and deployment.
It takes the output of the prepareSandbox task containing the built project with all its modules and dependencies, and the output of jarSearchableOptions task.
The produced archive is stored in the [buildDirectory]/distributions/archiveFile file. The archiveFile name and location can be controlled with properties provided with the Zip (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html) base task. By default, the archiveBaseName is set to the plugin name specified in the plugin.xml file, after it gets patched with the patchPluginXml task.
Do not repackage libraries into the main plugin archive, see "Plugin With Dependencies" in "Plugin Content".
The archive file which represents the output file produced by the task.
RegularFileProperty
buildPlugin.archiveFile
Depends on: patchPluginXml, prepareSandbox
Extends: JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), RunnableIdeAware ("RunnableIdeAware" in "Task Awares")
Sources: BuildSearchableOptionsTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/BuildSearchableOptionsTask.kt)
Builds the index of UI components (searchable options) for the plugin. This task runs a headless IDE instance to collect all the available options provided by the plugin's Settings.
If the plugin doesn't implement custom settings, it is recommended to disable this task via intellijPlatform.buildSearchableOptions ("buildSearchableOptions" in "IntelliJ Platform Extension") build feature.
In the case of running the task for the plugin using intellijPlatform.pluginConfiguration.productDescriptor ("Product Descriptor" in "IntelliJ Platform Extension"), a warning will be logged regarding potential issues with running headless IDE for paid plugins. It is possible to mute this warning with the paidPluginSearchableOptionsWarning ("paidPluginSearchableOptionsWarning" in "Gradle Properties") build feature.
The directory to which searchable options will be generated.
DirectoryProperty
[buildDirectory]/searchableOptions
Emit warning if the task is executed by a paid plugin. Can be disabled with the paidPluginSearchableOptionsWarning ("paidPluginSearchableOptionsWarning" in "Gradle Properties") build feature.
Property<Boolean>
paidPluginSearchableOptionsWarning ("paidPluginSearchableOptionsWarning" in "Gradle Properties") && productDescriptor is defined
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), IntelliJPlatformVersionAware ("IntelliJPlatformVersionAware" in "Task Awares")
Sources: InitializeIntelliJPlatformPluginTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/InitializeIntelliJPlatformPluginTask.kt)
This task is executed before every other task introduced by IntelliJ Platform Gradle Plugin to prepare it to run.
It is responsible for:
checking if the project uses IntelliJ Platform Gradle Plugin in the latest available version
preparing the KotlinX Coroutines Java Agent file to enable coroutines debugging when developing the plugin
The self-update check can be disabled via selfUpdateCheck ("selfUpdateCheck" in "Gradle Properties") build feature.
To make the Coroutines Java Agent available for the task, inherit from CoroutinesJavaAgentAware ("CoroutinesJavaAgentAware" in "Task Awares").
Determines if the operation is running in offline mode.
Depends on Gradle start parameters.
Property<Boolean>
StartParameter.isOffline
See also:
StartParameter (https://docs.gradle.org/current/javadoc/org/gradle/StartParameter.html)
Command Line Execution Options (https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_execution_options)
Represents the property for checking if self-update checks are enabled.
Property<Boolean>
selfUpdateCheck ("selfUpdateCheck" in "Gradle Properties")
Represents a lock file used to limit the plugin version checks in time. If the file is absent, and other conditions are met, the version check is performed.
RegularFileProperty
Java Agent file for the Coroutines library, which is required to enable coroutines debugging.
Property<Boolean>
[buildDirectory]/tmp/initializeIntelliJPlatformPlugin/coroutines-javaagent.jar
Represents the current version of the plugin.
Property<String>
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), JavaCompilerAware ("JavaCompilerAware" in "Task Awares")
Sources: InstrumentCodeTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/InstrumentCodeTask.kt)
Task dedicated to executing the code instrumentation using the Ant tasks provided with the currently used IntelliJ Platform dependency.
The code instrumentation scans the compiled Java and Kotlin classes for JetBrains Annotations usages to replace them with relevant functionalities they're responsible for.
This task is controlled with the intellijPlatform.instrumentCode ("instrumentCode" in "IntelliJ Platform Extension") extension property, enabled by default. To properly run the instrumentation, it is required to add instrumentationTools() ("Code Instrumentation" in "Dependencies Extension") dependencies to the project. This dependency is available via the intellijDependencies() ("Additional Repositories" in "Repositories Extension") repository, which can be added separately or using the defaultRepositories() ("Default Repositories" in "Repositories Extension") helper.
See also:
Compile classpath of the project's source set.
ConfigurableFileCollection
The list of directories with compiled classes.
ConfigurableFileCollection
classesDirs of the project's source sets.
The list of directories with GUI Designer form files.
ConfigurableFileCollection
.form files of the project's source sets.
Location of the source code.
ConfigurableFileCollection
Enables INFO logging when running Ant tasks.
Property<Boolean>
false
The output directory for instrumented classes.
DirectoryProperty
Depends on: jar, instrumentCode
Extends: Jar (https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html)
Creates a duplicate of the current module's jar file with instrumented classes added.
Depends on: buildSearchableOptions, patchPluginXml, prepareSandbox
Extends: Jar (https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html), PluginAware ("PluginAware" in "Task Awares"), SandboxAware ("SandboxAware" in "Task Awares")
Sources: JarSearchableOptionsTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/JarSearchableOptionsTask.kt)
Creates a JAR file with searchable options to be distributed with the plugin.
The directory where the JAR file will be created.
DirectoryProperty
[buildDirectory]/libs
The directory from which the prepared searchable options are read.
DirectoryProperty
buildSearchableOptions.outputDirectory
Emit a warning if no searchable options are found. Can be disabled with noSearchableOptionsWarning ("noSearchableOptionsWarning" in "Gradle Properties") build feature.
Property<Boolean>
noSearchableOptionsWarning ("noSearchableOptionsWarning" in "Gradle Properties")
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), IntelliJPlatformVersionAware ("IntelliJPlatformVersionAware" in "Task Awares")
Sources: PatchPluginXmlTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PatchPluginXmlTask.kt)
Represents an input plugin.xml file.
By default, a plugin.xml file is picked from the main resource location.
RegularFileProperty
src/main/resources/META-INF/plugin.xml
Represents the output plugin.xml file property for the task.
By default, the file is written to a temporary task-specific directory within the build directory.
RegularFileProperty
[buildDirectory]/tmp/patchPluginXml/plugin.xml
A unique identifier of the plugin.
It should be a fully qualified name similar to Java packages and must not collide with the ID of existing plugins. The ID is a technical value used to identify the plugin in the IDE and JetBrains Marketplace (https://plugins.jetbrains.com/).
Please use characters, numbers, and ./-/_ symbols only and keep it reasonably short.
The provided value will be set as a value of the <id> ("id" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.id ("id" in "IntelliJ Platform Extension")
The user-visible plugin display name. It should use Title Case.
The provided value will be set as a value of the <name> ("name" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.name ("name" in "IntelliJ Platform Extension")
The plugin version is displayed in the Plugins settings dialog and on the JetBrains Marketplace plugin page.
Plugins uploaded to the JetBrains Marketplace must follow semantic versioning.
The provided value will be set as a value of the <version> ("version" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.version ("version" in "IntelliJ Platform Extension")
The plugin description is displayed in the Plugins settings dialog and on the JetBrains Marketplace plugin page. Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed.
The description content is automatically wrapped with <![CDATA[... ]]>.
The provided value will be set as a value of the <description> ("description" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.description ("description" in "IntelliJ Platform Extension")
A short summary of new features, bugfixes, and changes provided with the latest plugin version. Change notes are displayed on the JetBrains Marketplace plugin page and in the Plugins settings dialog. Simple HTML elements, like text formatting, paragraphs, lists, etc., are allowed.
The change notes content is automatically wrapped with <![CDATA[... ]]>.
The provided value will be set as a value of the <change-notes> ("change-notes" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.changeNotes ("changeNotes" in "IntelliJ Platform Extension")
The plugin product code used in the JetBrains Sales System. The code must be agreed with JetBrains in advance and follow the requirements (https://plugins.jetbrains.com/docs/marketplace/obtain-a-product-code-from-jetbrains.html).
The provided value will be set as a value of the <product-descriptor code=""> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.productDescriptor.code ("code" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
Date of the major version release in the YYYYMMDD format.
The provided value will be set as a value of the <product-descriptor release-date=""> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.productDescriptor.releaseDate ("releaseDate" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
A major version in a special number format.
The provided value will be set as a value of the <product-descriptor release-version=""> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.productDescriptor.releaseVersion ("releaseVersion" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
The boolean value determining whether the plugin is a Freemium (https://plugins.jetbrains.com/docs/marketplace/freemium.html) plugin.
The provided value will be set as a value of the <product-descriptor optional=""> element attribute.
Property<Boolean>
intellijPlatform.pluginConfiguration.productDescriptor.optional ("optional" in "IntelliJ Platform Extension")
false
See also:
Plugin Configuration File: product-descriptor ("product-descriptor" in "Plugin Configuration File")
The lowest IDE version compatible with the plugin.
The provided value will be set as a value of the <idea-version since-build="..."/> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.ideaVersion.sinceBuild ("sinceBuild" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: idea-version ("idea-version" in "Plugin Configuration File")
The highest IDE version compatible with the plugin. Undefined value declares compatibility with all the IDEs since the version specified by the since-build (also with the future builds that may cause incompatibility errors).
The provided value will be set as a value of the <idea-version until-build="..."/> element attribute.
The until-build attribute can be unset by setting provider { null } as a value. Note that passing only null will make Gradle use a default value instead.
Property<String>
intellijPlatform.pluginConfiguration.ideaVersion.untilBuild ("untilBuild" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: idea-version ("idea-version" in "Plugin Configuration File")
The vendor name or organization ID (if created) in the Plugins settings dialog and on the JetBrains Marketplace plugin page.
The provided value will be set as a value of the <vendor> ("vendor" in "Plugin Configuration File") element.
Property<String>
intellijPlatform.pluginConfiguration.vendor.name ("name" in "IntelliJ Platform Extension")
The vendor's email address.
The provided value will be set as a value of the <vendor email=""> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.vendor.email ("email" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: vendor ("vendor" in "Plugin Configuration File")
The link to the vendor's homepage.
The provided value will be set as a value of the <vendor url=""> element attribute.
Property<String>
intellijPlatform.pluginConfiguration.vendor.url ("url" in "IntelliJ Platform Extension")
See also:
Plugin Configuration File: vendor ("vendor" in "Plugin Configuration File")
Depends on: jar, instrumentedJar
Extends: Sync (https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html), SandboxProducerAware ("SandboxProducerAware" in "Task Awares")
Sources: PrepareSandboxTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareSandboxTask.kt)
Prepares a sandbox environment with the installed plugin and its dependencies.
The sandbox directory is required by tasks that run IDE and tests in isolation from other instances, like when multiple IntelliJ Platforms are used for testing with runIde, testIde, testIdeUi, or testIdePerformance tasks.
To fully use the sandbox capabilities in a task, extend from SandboxAware ("SandboxAware" in "Task Awares") interface.
See also:
Extension: intellijPlatform.sandboxContainer ("sandboxContainer" in "IntelliJ Platform Extension")
Default sandbox destination directory where the plugin files will be copied.
DirectoryProperty
SandboxAware.sandboxPluginsDirectory ("sandboxPluginsDirectory" in "Task Awares")
The output of Jar task. The proper Jar.archiveFile is picked depending on if code instrumentation is enabled.
RegularFileProperty
Jar.archiveFile
See also:
Extension: intellijPlatform.instrumentCode ("instrumentCode" in "IntelliJ Platform Extension")
List of dependencies on external plugins resolved from the intellijPlatformPluginsExtracted configuration.
ConfigurableFileCollection
See also:
Dependencies Extension (Dependencies Extension)
Dependencies defined with the runtimeClasspath configuration.
ConfigurableFileCollection
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), TestableAware ("TestableAware" in "Task Awares")
Sources: PrepareTestTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrepareTestTask.kt)
This is a task used to prepare an immutable test task and provide all necessary dependencies and configuration for a proper testing configuration.
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), IntelliJPlatformVersionAware ("IntelliJPlatformVersionAware" in "Task Awares")
Sources: PrintBundledPluginsTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrintBundledPluginsTask.kt)
Prints the list of bundled plugins available within the currently targeted IntelliJ Platform.
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), ProductReleasesValueSource.FilterParameters ("ProductReleasesValueSource.FilterParameters" in "Types")
Sources: PrintProductsReleasesTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PrintProductsReleasesTask.kt)
Prints the list of binary product releases that, by default, match the currently selected IntelliJ Platform along with intellijPlatform.pluginConfiguration.ideaVersion.sinceBuild ("sinceBuild" in "IntelliJ Platform Extension") and intellijPlatform.pluginConfiguration.ideaVersion.untilBuild ("untilBuild" in "IntelliJ Platform Extension") properties.
The filter used for retrieving the release list can be customized by using properties provided with ProductReleasesValueSource.FilterParameters ("ProductReleasesValueSource.FilterParameters" in "Types").
Property holds the list of product releases to print.
Can be used to retrieve the result list.
ListProperty<String>
The output of ProductReleasesValueSource using default configuration
See also:
Types: ProductReleasesValueSource.FilterParameters ("ProductReleasesValueSource.FilterParameters" in "Types")
Depends on: buildPlugin, signPlugin
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html)
Sources: PublishPluginTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/PublishPluginTask.kt)
The task for publishing plugin to the remote plugins repository, such as JetBrains Marketplace (https://plugins.jetbrains.com).
See also:
Uploading a Plugin to JetBrains Marketplace ("Uploading a Plugin to JetBrains Marketplace" in "Publishing a Plugin")
Publishing Plugin With Gradle ("Publishing Plugin With Gradle" in "Publishing a Plugin")
Plugin upload API (https://plugins.jetbrains.com/docs/marketplace/plugin-upload.html)
ZIP archive to be published to the remote repository.
By default, it uses the output archiveFile of the signPlugin task if plugin signing is configured, otherwise the one from buildPlugin.
RegularFileProperty
signPlugin.archiveFile or buildPlugin.archiveFile
See also:
Extension: intellijPlatform.signing ("Signing" in "IntelliJ Platform Extension")
URL host of a plugin repository.
Property<String>
intellijPlatform.publishing.host ("host" in "IntelliJ Platform Extension")
Authorization token.
Property<String>
yes
intellijPlatform.publishing.token ("token" in "IntelliJ Platform Extension")
A list of channel names to upload plugin to.
ListProperty<String>
intellijPlatform.publishing.channels ("channels" in "IntelliJ Platform Extension")
Publish the plugin update and mark it as hidden to prevent public visibility after approval.
Property<String>
intellijPlatform.publishing.hidden ("hidden" in "IntelliJ Platform Extension")
See also:
Hidden release (https://plugins.jetbrains.com/docs/marketplace/hidden-plugin.html)
Specifies if the IDE Services plugin repository service should be used.
Property<String>
intellijPlatform.publishing.ideServices ("ideServices" in "IntelliJ Platform Extension")
Depends on: patchPluginXml, prepareSandbox
Extends: JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), RunnableIdeAware ("RunnableIdeAware" in "Task Awares"), CustomIntelliJPlatformVersionAware ("CustomIntelliJPlatformVersionAware" in "Task Awares")
Sources: RunIdeTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/RunIdeTask.kt)
Runs the IDE instance using the currently selected IntelliJ Platform with the built plugin loaded. It directly extends the JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) Gradle task, which allows for an extensive configuration (system properties, memory management, etc.).
This task class also inherits from CustomIntelliJPlatformVersionAware ("CustomIntelliJPlatformVersionAware" in "Task Awares"), which makes it possible to create runIde-like tasks using custom IntelliJ Platform versions:
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
import org.jetbrains.intellij.platform.gradle.tasks.RunIdeTask
tasks {
val runPhpStorm by registering(RunIdeTask::class) {
type = IntelliJPlatformType.PhpStorm
version = "2023.2.2"
}
val runLocalIde by registering(RunIdeTask::class) {
localPath = file("/Users/user/Applications/Android Studio.app")
}
}Depends on: buildPlugin
Extends: JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), SigningAware ("SigningAware" in "Task Awares")
Sources: SignPluginTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/SignPluginTask.kt)
Signs the ZIP archive with the provided key using the Marketplace ZIP Signer (https://github.com/JetBrains/marketplace-zip-signer) library.
To sign the plugin before publishing to JetBrains Marketplace (https://plugins.jetbrains.com) with the signPlugin task, it is required to provide a certificate chain and a private key with its password using intellijPlatform.signing ("Signing" in "IntelliJ Platform Extension") extension.
As soon as privateKey (or privateKeyFile) and certificateChain (or certificateChainFile properties are specified, the task will be executed automatically right before the publishPlugin task.
For more details, see Plugin Signing.
Input, unsigned ZIP archive file. Refers to in CLI option.
By default, it uses the output archive of the buildPlugin task.
RegularFileProperty
buildPlugin.archiveFile
Output, signed ZIP archive file. Refers to out CLI option.
Predefined with the name of the ZIP archive file with -signed name suffix attached. The output file is placed next to the input archiveFile.
RegularFileProperty
signPlugin.archiveFile with -signed suffix applied to the name
KeyStore file path. Refers to ks CLI option.
Property<String>
intellijPlatform.signing.keyStore ("keyStore" in "IntelliJ Platform Extension")
KeyStore password. Refers to ks-pass CLI option.
Property<String>
intellijPlatform.signing.keyStorePassword ("keyStorePassword" in "IntelliJ Platform Extension")
KeyStore key alias. Refers to ks-key-alias CLI option.
Property<String>
intellijPlatform.signing.keyStoreKeyAlias ("keyStoreKeyAlias" in "IntelliJ Platform Extension")
KeyStore type. Refers to ks-type CLI option.
Property<String>
intellijPlatform.signing.keyStoreType ("keyStoreType" in "IntelliJ Platform Extension")
JCA KeyStore Provider name. Refers to ks-provider-name CLI option.
Property<String>
intellijPlatform.signing.keyStoreProviderName ("keyStoreProviderName" in "IntelliJ Platform Extension")
Encoded private key in the PEM format. Refers to key CLI option.
Property<String>
intellijPlatform.signing.privateKey ("privateKey" in "IntelliJ Platform Extension")
A file with an encoded private key in the PEM format. Refers to key-file CLI option.
RegularFileProperty
intellijPlatform.signing.privateKeyFile ("privateKeyFile" in "IntelliJ Platform Extension")
Password required to decrypt the private key. Refers to key-pass CLI option.
Property<String>
intellijPlatform.signing.password ("password" in "IntelliJ Platform Extension")
A string containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert CLI option.
Property<String>
intellijPlatform.signing.certificateChain ("certificateChain" in "IntelliJ Platform Extension")
Path to the file containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert-file CLI option.
RegularFileProperty
intellijPlatform.signing.certificateChainFile ("certificateChainFile" in "IntelliJ Platform Extension")
Extends: Test (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html), TestableAware ("TestableAware" in "Task Awares"), CustomIntelliJPlatformVersionAware ("CustomIntelliJPlatformVersionAware" in "Task Awares")
Sources: TestIdeTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/TestIdeTask.kt)
This task is not registered with the testIde name, but its configuration extends the default test task.
Runs plugin tests against the currently selected IntelliJ Platform with the built plugin loaded. It directly extends the Test (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html) Gradle task, which allows for an extensive configuration (system properties, memory management, etc.).
This task class also inherits from CustomIntelliJPlatformVersionAware ("CustomIntelliJPlatformVersionAware" in "Task Awares"), which makes it possible to create testIde-like tasks using custom IntelliJ Platform versions:
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
import org.jetbrains.intellij.platform.gradle.tasks.TestIdeTask
tasks {
val testPhpStorm by registering(TestIdeTask::class) {
type = IntelliJPlatformType.PhpStorm
version = "2023.2.2"
}
val testLocalIde by registering(TestIdeTask::class) {
localPath = file("/Users/user/Applications/Android Studio.app")
}
}Not implemented.
Not implemented.
Depends on: patchPluginXml
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), IntelliJPlatformVersionAware ("IntelliJPlatformVersionAware" in "Task Awares"), PluginAware ("PluginAware" in "Task Awares")
Sources: VerifyPluginProjectConfigurationTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginProjectConfigurationTask.kt)
Validates the plugin project configuration:
The patchPluginXml.sinceBuild property can't be lower than the target IntelliJ Platform major version.
The Java/Kotlin sourceCompatibility and targetCompatibility properties should align Java versions required by patchPluginXml.sinceBuild and the currently used IntelliJ Platform.
The Kotlin API version should align the version required by patchPluginXml.sinceBuild and the currently used IntelliJ Platform.
The used IntelliJ Platform version should be higher than 2022.3 (223.0).
The dependency on the "Kotlin Standard Library (stdlib)" in "Configuring Kotlin Support" should be excluded.
The Kotlin plugin in version 1.8.20 is not used with IntelliJ Platform Gradle Plugin due to the 'java.lang.OutOfMemoryError: Java heap space' exception.
The Kotlin Coroutines library should not be added explicitly to the project as it is already provided with the IntelliJ Platform.
The IntelliJ Platform cache directory should be excluded from the version control system. Add the .intellijPlatform'` entry to the .gitignore file.
The currently selected Java Runtime is not JetBrains Runtime (JBR).
For more details regarding the Java version used in the specific IntelliJ SDK, see Build Number Ranges.
See also:
Report directory where the verification result will be stored.
DirectoryProperty
[buildDirectory]/reports/verifyPluginConfiguration
Root project path.
Property<File>
[rootProject]
IntelliJ Platform cache directory.
Property<File>
intellijPlatform.cachePath ("cachePath" in "IntelliJ Platform Extension")
The .gitignore file located in the [rootDirectory], tracked for content change.
Property<File>
[rootProject]/.gitignore
The JavaCompile.sourceCompatibility property value defined in the build script.
Property<String>
JavaCompile.sourceCompatibility
The JavaCompile.targetCompatibility property value defined in the build script.
Property<String>
JavaCompile.targetCompatibility
Indicates that the Kotlin Gradle Plugin is loaded and available.
Property<Boolean>
Kotlin Gradle Plugin presence
The apiVersion property value of compileKotlin.kotlinOptions defined in the build script.
Property<String?>
compileKotlin.kotlinOptions.apiVersion
The languageVersion property value of compileKotlin.kotlinOptions defined in the build script.
Property<String?>
compileKotlin.kotlinOptions.languageVersion
The version of the Kotlin used in the project.
Property<String?>
kotlin.coreLibrariesVersion
The jvmTarget property value of compileKotlin.kotlinOptions defined in the build script.
Property<String?>
compileKotlin.kotlinOptions.jvmTarget
kotlin.stdlib.default.dependency property value defined in the gradle.properties file.
Property<Boolean>
kotlin.stdlib.default.dependency Gradle property
This variable represents whether the Kotlin Coroutines library is added explicitly to the project dependencies.
Property<Boolean>
The org.jetbrains.kotlinx:kotlinx-coroutines dependency presence
Extends: JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), SigningAware ("SigningAware" in "Task Awares")
Sources: PrepareSandboxTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginSignatureTask.kt)
Validates the signature of the plugin archive file using the Marketplace ZIP Signer (https://github.com/JetBrains/marketplace-zip-signer) library.
See also:
Marketplace ZIP Signer (https://github.com/JetBrains/marketplace-zip-signer)
Input, unsigned ZIP archive file. Refers to in CLI option.
RegularFileProperty
signPlugin.signedArchiveFile
A string containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert CLI option.
Takes precedence over the certificateChainFile property.
Property<String>
Path to the file containing X509 certificates. The first certificate from the chain will be used as a certificate authority (CA). Refers to cert-file CLI option.
RegularFileProperty
signPlugin.certificateChainFile or signPlugin.certificateChain written to a temporary file
Depends on: prepareSandbox
Extends: DefaultTask (https://docs.gradle.org/current/dsl/org.gradle.api.DefaultTask.html), SandboxAware ("SandboxAware" in "Task Awares")
Sources: VerifyPluginStructureTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginStructureTask.kt)
Validates completeness and contents of plugin.xml descriptors as well as plugin archive structure.
See also:
Specifies whether the build should fail when the verifications performed by this task fail.
Property<Boolean>
false
Specifies whether the build should fail when the verifications performed by this task emit unacceptable warnings.
Property<Boolean>
false
Specifies whether the build should fail when the verifications performed by this task emit warnings.
Property<Boolean>
true
The location of the built plugin file which will be used for verification.
DirectoryProperty
prepareSandbox.defaultDestinationDirectory/intellijPlatform.pluginConfiguration.name ("name" in "IntelliJ Platform Extension")
Depends on: buildPlugin
Extends: JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), RuntimeAware ("RuntimeAware" in "Task Awares"), PluginVerifierAware ("PluginVerifierAware" in "Task Awares")
Sources: VerifyPluginTask (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginTask.kt)
Runs the IntelliJ Plugin Verifier CLI tool to check the binary compatibility with specified IDE builds.
See also:
Extension: intellijPlatform.verifyPlugin ("Verify Plugin" in "IntelliJ Platform Extension")
Types: FailureLevel ("FailureLevel" in "Types")
Types: Subsystems ("Subsystems" in "Types")
Types: VerificationReportsFormats ("VerificationReportsFormats" in "Types")
Verifying Plugin Compatibility (Verifying Plugin Compatibility)
IntelliJ Plugin Verifier (https://github.com/JetBrains/intellij-plugin-verifier)
Holds a reference to IntelliJ Platform IDEs which will be used by the IntelliJ Plugin Verifier CLI tool for the binary plugin verification.
The list of IDEs is controlled with the intellijPlatform.verifyPlugin.ides ("Verify Plugin IDEs" in "IntelliJ Platform Extension") extension.
ConfigurableFileCollection
Input ZIP archive file of the plugin to verify. If empty, the task will be skipped.
RegularFileProperty
buildPlugin.archiveFile
The list of class prefixes from the external libraries. The Plugin Verifier will not report No such class for classes of these packages.
ListProperty<String>
intellijPlatform.verifyPlugin.externalPrefixes ("externalPrefixes" in "IntelliJ Platform Extension")
Defines the verification level at which the task should fail if any reported issue matches.
ListProperty<FailureLevel> ("FailureLevel" in "Types")
intellijPlatform.verifyPlugin.failureLevel ("failureLevel" in "IntelliJ Platform Extension")
The list of free arguments is passed directly to the IntelliJ Plugin Verifier CLI tool.
They can be used in addition to the arguments that are provided by dedicated options.
ListProperty<String>
intellijPlatform.verifyPlugin.freeArgs ("freeArgs" in "IntelliJ Platform Extension")
A file that contains a list of problems that will be ignored in a report.
RegularFileProperty
intellijPlatform.verifyPlugin.ignoredProblemsFile ("ignoredProblemsFile" in "IntelliJ Platform Extension")
Determines if the operation is running in offline mode.
Depends on Gradle start parameters
Property<Boolean>
StartParameter.isOffline
See also:
StartParameter (https://docs.gradle.org/current/javadoc/org/gradle/StartParameter.html)
Command Line Execution Options (https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_execution_options)
Specifies which subsystems of IDE should be checked.
Subsystems ("Subsystems" in "Types")
intellijPlatform.verifyPlugin.subsystemsToCheck ("subsystemsToCheck" in "IntelliJ Platform Extension")
A flag that controls the output format. If set to true, the TeamCity (https://www.jetbrains.com/teamcity/) compatible output will be returned to stdout.
Property<Boolean>
intellijPlatform.verifyPlugin.teamCityOutputFormat ("teamCityOutputFormat" in "IntelliJ Platform Extension")
The path to the directory where verification reports will be saved.
DirectoryProperty
intellijPlatform.verifyPlugin.verificationReportsDirectory ("verificationReportsDirectory" in "IntelliJ Platform Extension")
The output formats of the verification reports.
ListProperty<VerificationReportsFormats> ("VerificationReportsFormats" in "Types")
intellijPlatform.verifyPlugin.verificationReportsFormats ("verificationReportsFormats" in "IntelliJ Platform Extension")
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
The Task Awares is a set of interfaces that can be applied to custom Gradle tasks and, when registered using the dedicated register method, inject new features or properties with predefined values.
IntelliJ Platform Gradle Plugin supports creating custom tasks which can use *Aware interfaces. Example:
abstract class RetrievePluginNameTask : DefaultTask(), PluginAware
val retrievePluginName by tasks.registering(RetrievePluginNameTask::class) {
val outputFile = layout.buildDirectory.file("pluginName.txt")
doLast {
outputFile.get().asFile.writeText(pluginXml.parse { name }.get())
}
}Inherited by: RunnableIdeAware
Sources: AutoReloadAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/AutoReloadAware.kt)
Provides the possibility to auto-reload plugin when run in the IDE.
Enables auto-reload of dynamic plugins. Dynamic plugin will be reloaded automatically when its content is modified.
This allows a much faster development cycle by avoiding a full restart of the development instance after code changes.
Property<Boolean>
intellijPlatform.autoReload ("autoReload" in "IntelliJ Platform Extension")
Depends on: initializeIntelliJPlatformPlugin ("initializeIntelliJPlatformPlugin" in "Tasks")
Inherited by: RunnableIdeAware
Sources: CoroutinesJavaAgentAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/CoroutinesJavaAgentAware.kt)
Provides the path to the Java Agent file for the Coroutines library required to enable coroutines debugging.
The path to the coroutines Java Agent file.
RegularFileProperty
initializeIntellijPlatformPlugin.coroutinesJavaAgent ("coroutinesJavaAgent" in "Tasks")
Depends on: IntelliJPlatformVersionAware
Inherited by: SandboxAware, runIde ("runIde" in "Tasks"), testIde ("testIde" in "Tasks"), testIdePerformance ("testIdePerformance" in "Tasks"), testIdeUi ("testIdeUi" in "Tasks")
Sources: CustomIntelliJPlatformVersionAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/CustomIntelliJPlatformVersionAware.kt)
By default, the project with the IntelliJ Platform Gradle Plugin applied required the presence of the IntelliJ Platform, referred to later by various tasks, configurations, and extensions.
The custom IntelliJ Platform concept allows using another version, i.e., to run a guest IDE or tests against it.
When applying this interface to the task, custom configurations to hold new dependencies defined by type and version (or localPath, if referring to the local IntelliJ Platform instance) are created, as well as a dedicated prepareSandbox ("prepareSandbox" in "Tasks") task.
Configurations, as well as the task preparing sandbox for running and testing the custom IntelliJ Platform (if required), have a random suffix applied to avoid collisions.
An input property to configure the type of the custom IntelliJ Platform.
By default, it refers to the IntelliJ Platform type used by the current project.
IntelliJPlatformType ("IntelliJPlatformType" in "Types")
An input property to configure the version of the custom IntelliJ Platform.
By default, it refers to the IntelliJ Platform version used by the current project.
Property<String>
An input property to define the path to the local IntelliJ Platform instance to configure the version of the custom IntelliJ Platform.
The local path precedes the IntelliJ Platform resolution using the type and version properties.
DirectoryProperty
Inherited by: CustomIntelliJPlatformVersionAware, RuntimeAware, SandboxAware, SplitModeAware, initializeIntelliJPlatformPlugin ("initializeIntelliJPlatformPlugin" in "Tasks"), patchPluginXml ("patchPluginXml" in "Tasks"), printBundledPlugins ("printBundledPlugins" in "Tasks"), verifyPluginProjectConfiguration ("verifyPluginProjectConfiguration" in "Tasks")
Sources: IntelliJPlatformVersionAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/IntelliJPlatformVersionAware.kt)
Provides tasks a possibility for accessing information about the IntelliJ Platform currently used in the project.
The intelliJPlatformConfiguration input property receives a dependency added to the intellijPlatform configuration, which eventually is resolved and lets to access the IntelliJ Platform details such as ProductInfo ("ProductInfo" in "Types") or the path to the IntelliJ Platform directory.
It is required to have a dependency on the IntelliJ Platform added to the project with helpers available in Dependencies Extension.
Holds the intellijPlatform configuration with the IntelliJ Platform dependency added.
It should not be directly accessed.
ConfigurableFileCollection
Provides a direct path to the IntelliJ Platform dependency artifact.
Read-only
Path
Provides information about the IntelliJ Platform product.
The information is retrieved from the product-info.json file in the IntelliJ Platform directory.
Read-only
ProductInfo ("ProductInfo" in "Types")
Validates that the resolved IntelliJ Platform is supported by checking against the minimal supported IntelliJ Platform version.
Invokes ProductInfo.validateSupportedVersion() ("validateSupportedVersion()" in "Types").
IllegalArgumentException
Inherited by: instrumentCode ("instrumentCode" in "Tasks")
Sources: JavaCompilerAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/JavaCompilerAware.kt)
Provides the dependency on Java Compiler required for the code instrumentation ("instrumentCode" in "Tasks") to properly configure Ant tasks provided by the IntelliJ Platform.
Holds the intellijPlatformJavaCompiler configuration with the Java Compiler dependency added.
ConfigurableFileCollection
Depends on: patchPluginXml ("patchPluginXml" in "Tasks")
Inherited by: TestableAware, RunnableIdeAware, jarSearchableOptions ("jarSearchableOptions" in "Tasks")
Sources: PluginAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/PluginAware.kt)
Provides information about the currently built plugin.
It resolves and parses the final plugin.xml descriptor file, making its details easily accessible.
Holds the path to the patched plugin.xml file.
RegularPropertyFile
patchPluginXml.outputFile ("outputFile" in "Tasks")
The parse method provides a possibility for parsing the pluginXml file and direct access to the PluginBean ("PluginBean" in "Types") object.
Should be used along with the pluginXml property like:
abstract class RetrievePluginNameTask : DefaultTask(), PluginAware
val retrievePluginName by tasks.registering(RetrievePluginNameTask::class) {
doLast {
val name = pluginXml.parse { name }.get()
println("Plugin Name: $name")
}
}Inherited by: verifyPlugin ("verifyPlugin" in "Tasks")
Sources: PluginVerifierAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/PluginVerifierAware.kt)
Provides the path to the IntelliJ Plugin Verifier executable.
It is required to have a dependency on the IntelliJ Plugin Verifier added to the project with intellijPlatform.pluginVerifier() (Dependencies Extension) dependencies extension.
Path to the IntelliJ Plugin Verifier executable.
RegularFileProperty
Depends on: AutoReloadAware, CoroutinesJavaAgentAware, PluginAware, RuntimeAware, SandboxAware, SplitModeAware, initializeIntelliJPlatformPlugin ("initializeIntelliJPlatformPlugin" in "Tasks")
Inherited by: buildSearchableOptions ("buildSearchableOptions" in "Tasks"), runIde ("runIde" in "Tasks"), testIde ("testIde" in "Tasks"), testIdePerformance ("testIdePerformance" in "Tasks"), testIdeUi ("testIdeUi" in "Tasks")
Sources: RunnableIdeAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/RunnableIdeAware.kt)
The interface which uses a set of various interfaces required for running a guest IDE. Inherits from:
CoroutinesJavaAgentAware
PluginAware
RuntimeAware
SandboxAware
JavaForkOptions
Inherited by: RunnableIdeAware, TestableAware, verifyPlugin ("verifyPlugin" in "Tasks")
Sources: RuntimeAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/RuntimeAware.kt)
Provides access to the Java Runtime (i.e., JetBrains Runtime) resolved with RuntimeResolver.
Java Runtime parent directory.
DirectoryProperty
An architecture of the Java Runtime currently used for running Gradle.
Property<String>
Metadata object of the Java Runtime currently used for running Gradle.
Property<String>
A custom JavaLauncher instance configured with the resolved runtimeDirectory.
Property<String>
Depends on: prepareSandbox ("prepareSandbox" in "Tasks")
Inherited by: RunnableIdeAware, SandboxProducerAware, jarSearchableOptions ("jarSearchableOptions" in "Tasks"), prepareSandbox ("prepareSandbox" in "Tasks"), verifyPluginStructure ("verifyPluginStructure" in "Tasks")
Sources: SandboxAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt)
Provides quick access to the sandbox container and specific directories located within it.
The path to the sandbox container is obtained using the intellijPlatform.sandboxContainer ("sandboxContainer" in "IntelliJ Platform Extension") extension property and the type and version of the IntelliJ Platform applied to the project.
Paths respect custom IntelliJ Platform when combined with CustomIntelliJPlatformVersionAware.
Represents the suffix used i.e., for test-related tasks.
Property<String>
The container for all sandbox-related directories.
The directory name depends on the platform type and version currently used for running a task.
DirectoryProperty
A configuration directory located within the sandboxContainerDirectory.
DirectoryProperty
A plugins directory located within the sandboxContainerDirectory.
DirectoryProperty
A system directory located within the sandboxContainerDirectory.
DirectoryProperty
A log directory located within the sandboxContainerDirectory.
DirectoryProperty
Depends on: SandboxAware
Inherited by: prepareSandbox ("prepareSandbox" in "Tasks")
Sources: SandboxProducerAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxProducerAware.kt)
Allows distinguishing between the SandboxAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SandboxAware.kt) consumers and producers.
Inherited by: signPlugin ("signPlugin" in "Tasks"), verifyPluginSignature ("verifyPluginSignature" in "Tasks")
Sources: SigningAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SigningAware.kt)
Provides the path to the Marketplace ZIP Signer executable.
It is required to have a dependency on the Marketplace ZIP Signer added to the project with intellijPlatform.zipSigner() (Dependencies Extension) dependencies extension.
Path to the Marketplace ZIP Signer executable.
RegularFileProperty
Inherited by: RunnableIdeAware
Sources: SplitModeAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/SplitModeAware.kt)
When you develop a plugin, you may want to check how it works in remote development mode, when one machine is running the backend part and another is running a frontend part (JetBrains Client) which connects to the backend.
This property allows running the IDE with backend and frontend parts running in separate processes. The developed plugin is installed in the backend part.
Split Mode requires the IntelliJ Platform in the version 241.14473 or later.
Inherited by: prepareTest ("prepareTest" in "Tasks"), testIde ("testIde" in "Tasks")
Sources: TestableAware (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/aware/TestableAware.kt)
Interface used to describe tasks used for running tests, such as a customizable testIde ("testIde" in "Tasks") or prepareTest ("prepareTest" in "Tasks") used for configuring test and keeping it immutable.
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
This page lists various types used to configure Tasks.
VerifyPluginTask.FailureLevel (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginTask.kt)
Enum class describing the failure level of the IntelliJ Plugin Verifier CLI tool run with the verifyPlugin ("verifyPlugin" in "Tasks") task.
Name | Description |
|---|---|
COMPATIBILITY_WARNINGS | Compatibility warnings detected against the specified IDE version. |
COMPATIBILITY_PROBLEMS | Compatibility problems detected against the specified IDE version. |
DEPRECATED_API_USAGES | Plugin uses API marked as deprecated (@Deprecated). |
SCHEDULED_FOR_REMOVAL_API_USAGES | Plugin uses API marked as scheduled for removal (ApiStatus.@ScheduledForRemoval). |
EXPERIMENTAL_API_USAGES | Plugin uses API marked as experimental (ApiStatus.@Experimental). |
INTERNAL_API_USAGES | Plugin uses API marked as internal (ApiStatus.@Internal). |
OVERRIDE_ONLY_API_USAGES | Override-only API is used incorrectly (ApiStatus.@OverrideOnly). |
NON_EXTENDABLE_API_USAGES | Non-extendable API is used incorrectly (ApiStatus.@NonExtendable). |
PLUGIN_STRUCTURE_WARNINGS | The structure of the plugin is not valid. |
MISSING_DEPENDENCIES | Plugin has some dependencies missing. |
INVALID_PLUGIN | Provided plugin artifact is not valid. |
NOT_DYNAMIC | Plugin probably cannot be enabled or disabled without IDE restart |
ALL | Contains all possible options. |
NONE | Contains no option. |
See also:
Extension: intellijPlatform.verifyPlugin.failureLevel ("failureLevel" in "IntelliJ Platform Extension")
Tasks: verifyPlugin.failureLevel ("failureLevel" in "Tasks")
IntelliJPlatformType (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/IntelliJPlatformType.kt)
Describes all IntelliJ Platform types available to be used for plugin development, dependency resolution, and plugin verification.
Each entry is composed of a product code and coordinates used for dependency and binary release resolution.
Name | Code | Artifact Coordinates | Binary release |
|---|---|---|---|
AndroidStudio | AI | com.google.android.studio:studio | ✓ |
Aqua | QA | - | ✓ |
CLion | CL | com.jetbrains.intellij.clion:clion | ✓ |
DataGrip | DB | - | ✓ |
DataSpell | DS | - | ✓ |
FleetBackend | FLIJ | com.jetbrains.intellij.fleetBackend:fleetBackend | |
Gateway | GW | com.jetbrains.intellij.gateway:gateway | ✓ |
GoLand | GO | com.jetbrains.intellij.goland:goland | ✓ |
IntellijIdeaCommunity | IC | com.jetbrains.intellij.idea:ideaIC | ✓ |
IntellijIdeaUltimate | IU | com.jetbrains.intellij.idea:ideaIU | ✓ |
MPS | MPS | - | ✓ |
PhpStorm | PS | com.jetbrains.intellij.phpstorm:phpstorm | ✓ |
PyCharmProfessional | PY | com.jetbrains.intellij.pycharm:pycharmPY | ✓ |
PyCharmCommunity | PC | com.jetbrains.intellij.pycharm:pycharmPC | ✓ |
Rider | RD | com.jetbrains.intellij.rider:riderRD | ✓ |
RubyMine | RM | - | ✓ |
RustRover | RR | com.jetbrains.intellij.rustrover:RustRover | ✓ |
WebStorm | WS | com.jetbrains.intellij.webstorm:webstorm | ✓ |
Writerside | WRS | com.jetbrains.intellij.idea:writerside | ✓ |
Describes the content of the plugin.xml file.
See also:
Represents information about the IntelliJ Platform product.
The information is retrieved from the product-info.json file in the IntelliJ Platform directory.
Name | Description |
|---|---|
name | The product's name, like "IntelliJ IDEA". |
version | The marketing version of the product, like "2023.2". |
versionSuffix | The suffix of the version, like "EAP". |
buildNumber | The build number of the product, like "232.8660.185". |
productCode | The product code, like "IU". |
dataDirectoryName | The directory name of the product data. |
svgIconPath | The path to the SVG icon of the product. |
productVendor | The vendor of the product. |
launch | The list of OS- and arch-specific launch configurations for the product. |
customProperties | The list of custom properties of the product. |
bundledPlugins | The list of bundled plugins provided with the current release. |
fileExtensions | The list of file extensions associated with the product. |
modules | The list of modules of the product. |
Validates that the resolved IntelliJ Platform is supported by checking against the minimal supported IntelliJ Platform version.
If the provided version is lower, an IllegalArgumentException is thrown with an appropriate message.
IllegalArgumentException
ProductRelease.Channel (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/models/ProductRelease.kt)
List of available channels used by JetBrains IDEs and Android Studio (Android Studio Plugin Development) for describing binary releases.
Name | JetBrains IDEs | Android Studio |
|---|---|---|
RELEASE | ✓ | ✓ |
EAP | ✓ | |
MILESTONE | ✓ | |
BETA | ✓ | |
CANARY | ✓ | |
PATCH | ✓ | |
RC | ✓ | |
PREVIEW | ✓ |
See also:
Extension: intellijPlatform.verifyPlugin.ides ("Verify Plugin IDEs" in "IntelliJ Platform Extension")
Tasks: printProductsReleases ("printProductsReleases" in "Tasks")
ProductReleasesValueSource.FilterParameters (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/providers/ProductReleasesValueSource.kt)
Interface that provides a clear way to filter binary product releases for IntelliJ Plugin Verifier.
Name | Description |
|---|---|
sinceBuild | Build number from which the binary IDE releases will be matched. |
untilBuild | Build number until which the binary IDE releases will be matched. |
types | A list of IntelliJPlatformType types to match. |
channels | A list of ProductRelease.Channel types of binary releases to search in. |
See also:
Extension: intellijPlatform.verifyPlugin.ides ("Verify Plugin IDEs" in "IntelliJ Platform Extension")
Tasks: printProductsReleases ("printProductsReleases" in "Tasks")
VerifyPluginTask.Subsystems (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginTask.kt)
Specify which subsystems of the IDE should be checked by the IntelliJ Plugin Verifier CLI tool run with the verifyPlugin ("verifyPlugin" in "Tasks") task.
Name | Description |
|---|---|
ALL | Verify all code. |
ANDROID_ONLY | Verify only code related to Android support. |
WITHOUT_ANDROID | Exclude problems related to Android support. |
See also:
Extension: intellijPlatform.verifyPlugin.subsystemsToCheck ("subsystemsToCheck" in "IntelliJ Platform Extension")
Tasks: verifyPlugin.subsystemsToCheck ("subsystemsToCheck" in "Tasks")
TestFrameworkType (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/TestFrameworkType.kt)
Allows for adding test-framework testing library variants. See Dependencies Extension: Testing ("Testing" in "Dependencies Extension").
Name | Coordinates |
|---|---|
Platform.JUnit4 | com.jetbrains.intellij.platform:test-framework |
Platform.JUnit5 | com.jetbrains.intellij.platform:test-framework-junit5 |
Platform.Bundled | [platformPath]/lib/testFramework.jar |
Plugin.Go | com.jetbrains.intellij.go:go-test-framework |
Plugin.Ruby | com.jetbrains.intellij.idea:ruby-test-framework |
Plugin.Java | com.jetbrains.intellij.java:java-test-framework |
Plugin.JavaScript | com.jetbrains.intellij.javascript:javascript-test-framework |
Plugin.Maven | com.jetbrains.intellij.maven:maven-test-framework |
Plugin.ReSharper | com.jetbrains.intellij.resharper:resharper-test-framework |
The Platform.Bundled type should not be used unless it is necessary, like in the case of Rider, as its test-framework is not published as an artifact.
VerifyPluginTask.VerificationReportsFormats (https://github.com/JetBrains/gradle-intellij-plugin/blob/2.0/src/main/kotlin/org/jetbrains/intellij/platform/gradle/tasks/VerifyPluginTask.kt)
Enum class describing the type of the results produced by the IntelliJ Plugin Verifier CLI tool run with the verifyPlugin ("verifyPlugin" in "Tasks") task.
Name | Description |
|---|---|
PLAIN | Plain text file. |
HTML | HTML formatted output file. |
MARKDOWN | Markdown file. |
ALL | Contains all possible options. |
NONE | Contains no option. |
See also:
Extension: intellijPlatform.verifyPlugin.verificationReportsFormats ("verificationReportsFormats" in "IntelliJ Platform Extension")
Tasks: verifyPlugin.verificationReportsFormats ("verificationReportsFormats" in "Tasks")
Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Early Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
The IntelliJ Platform Gradle Plugin exposes a number of build features to control some of the low-level Gradle plugin behaviors. To enable or disable a particular feature, add a Project property to the gradle.properties file with the following pattern:
org.jetbrains.intellij.platform.<name>=<value>The plugin uses a dedicated cache directory to store files related to the current project configuration files, such as:
XML files generated for the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") local Ivy repository
coroutines Java agent file created by the initializeIntelliJPlatformPlugin ("initializeIntelliJPlatformPlugin" in "Tasks") task
[rootProject]/.intellijPlatform
org.jetbrains.intellij.platform.intellijPlatformCache=/path/to/intellijPlatformCache/The localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry applied to the repositories {} block is required to apply to the project dependencies that need extra pre-processing before they can be correctly used by the IntelliJ Platform Gradle Plugin and loaded by Gradle.
This is resolved by creating an Ivy XML file in a dedicated directory pointed by the localPlatformArtifacts property.
intellijPlatformCache/ivy/
org.jetbrains.intellij.platform.localPlatformArtifacts=/path/to/localPlatformArtifacts/Build features are Gradle properties defined by the IntelliJ Platform Gradle Plugin to control specific features. Such properties have a simplified form:
org.jetbrains.intellij.platform.buildFeature.<buildFeatureName>=<true|false>E.g., to disable the feature, add this line:
org.jetbrains.intellij.platform.buildFeature.selfUpdateCheck=falseInstruct the IDE that sources are needed to be downloaded when working with IntelliJ Platform Gradle Plugin.
Value is passed directly to the Idea Gradle Plugin (https://docs.gradle.org/current/userguide/idea_plugin.html) to the idea.module.downloadSources property.
See also:
IdeaModule.downloadSources (https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.idea.model.IdeaModule.html#org.gradle.plugins.ide.idea.model.IdeaModule:downloadSources)
true
org.jetbrains.intellij.platform.buildFeature.downloadSources=falseWhen the "buildSearchableOptions" in "Tasks" doesn't produce any results, e.g., when the plugin doesn't implement any Settings (Settings), a warning is shown to suggest disabling it for better performance with "buildSearchableOptions" in "IntelliJ Platform Extension".
true
org.jetbrains.intellij.platform.buildFeature.buildSearchableOptions=falseDue to IDE limitations, it is impossible to run the IDE in headless mode to collect searchable options for a paid plugin. As paid plugins require providing a valid license and presenting a UI dialog, it is impossible to handle such a case, and the task will fail. This feature flag displays the given warning when the task is run by a paid plugin.
true
org.jetbrains.intellij.platform.buildFeature.paidPluginSearchableOptionsWarning=falseChecks whether the currently used IntelliJ Platform Gradle Plugin is outdated and if a new release is available. The plugin performs an update check on every run asking the GitHub Releases page for the redirection URL to the latest version with HEAD HTTP request: https://github.com/jetbrains/gradle-intellij-plugin/releases/latest.
If the current version is outdated, the plugin will emit a warning with its current and the latest version.
Feature respects the Gradle --offline (https://docs.gradle.org/current/userguide/command_line_interface.html#sec:command_line_execution_options) mode.
It is strongly suggested to always use the latest available version. Older plugin versions may also not fully support the latest IDE releases.
true
org.jetbrains.intellij.platform.buildFeature.selfUpdateCheck=falseBy default, JetBrains Cache Redirector is used when resolving Maven repositories or any resources used by the IntelliJ Platform Gradle Plugin. Due to limitations, sometimes it is desired to limit the list of remote endpoints accessed by Gradle.
It is possible to refer to the direct location (whenever it is possible) by switching off JetBrains Cache Redirector globally.
true
org.jetbrains.intellij.platform.buildFeature.useCacheRedirector=falseSome dependencies are tied to IntelliJ Platform build numbers and hosted in the IntelliJ Dependencies Repository. Despite this, certain versions (like EAP or nightly builds) might be absent.
To solve this, we fetch a list of all versions from the Maven repository and locate the closest match. This method requires an additional remote repository request. If undesired, this feature can be disabled to strictly match dependencies to your build version.
true
org.jetbrains.intellij.platform.buildFeature.useClosestVersionResolving=falseEarly Access StatusIntelliJ Platform Gradle Plugin 2.0.0 is currently in Beta and may not support all features and project setups yet (see also "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)"). Please report bugs or problems in the GitHub issue tracker or Slack channel (see here (IntelliJ Platform Gradle Plugin 2.x (Beta))).
Any documentation issues should be reported using the feedback form on the bottom of this page. Please leave your email in case we need more details.
Thanks a lot in advance for your feedback!
As the 2.x branch brings significant breaking changes to the plugin, the name was changed from Gradle IntelliJ Plugin to IntelliJ Platform Gradle Plugin as the old one was confused with the bundled Gradle support plugin in the IDE. The plugin is published to the Gradle Plugin Portal with a new name as a new entry, and the old one is marked as deprecated.
The minimum required Gradle version is now 8.2 running on Java 17 or later. See "Requirements" in "IntelliJ Platform Gradle Plugin 2.x (Beta)".
Plugin ID has changed from org.jetbrains.intellij to org.jetbrains.intellij.platform. To apply it, use:
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
}The 2.x release introduces multiple Plugins, featuring a dedicated one to help with migration from Gradle IntelliJ Plugin 1.x: "Migration" in "Plugins".
When you apply the , Gradle will fail to process the build.gradle.kts file as the old doesn't exist anymore. To fill all gaps and help users figure out required changes - right in the IDE - the org.jetbrains.intellij.platform.migration plugin was introduced:
plugins {
id("org.jetbrains.intellij.platform") version "2.0.0-beta1"
id("org.jetbrains.intellij.platform.migration") version "2.0.0-beta1"
}The intellij {} extension is no longer available and was replaced with intellijPlatform {}. Note that the available properties differ, see IntelliJ Platform Extension for details.
Use: intellijPlatform.pluginConfiguration.name ("name" in "IntelliJ Platform Extension"):
intellijPlatform {
pluginConfiguration {
name = ...
}
}Define the IntelliJ Platform dependency in dependencies{} block:
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
create(type, version)
}
}The intellij.plugins property is no longer available.
Bundled plugins are now defined separately from plugins of other sources (like JetBrains Marketplace).
Define dependencies on plugins or bundled plugins in dependencies {} block instead:
Example:
Setting up dependencies on comma-separated plugins listed in platformPlugins and platformBundledPlugins properties from gradle.properties.
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
plugins(providers.gradleProperty("platformPlugins").map { it.split(',') })
bundledPlugins(providers.gradleProperty("platformBundledPlugins").map { it.split(',') })
}
}See: "Plugins" in "Dependencies Extension"
localPlatformArtifacts() and defaultRepositories()Note that unless using recommended default defaultRepositories() ("Default Repositories" in "Repositories Extension"), the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry needs to be added to the repositories {} block explicitly to use local dependencies (bundled plugins, local IDE, etc.).
Define dependencies on local IDE instance in dependencies {} block:
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
local(localPath)
}
}See: "Custom Target Platforms" in "Dependencies Extension"
localPlatformArtifacts() and defaultRepositories()Note that unless using recommended default defaultRepositories() ("Default Repositories" in "Repositories Extension"), the localPlatformArtifacts() ("Additional Repositories" in "Repositories Extension") entry needs to be added to the repositories {} block explicitly to use local dependencies (bundled plugins, local IDE, etc.).
The plugin.xml file is now fully managed by the intellijPlatform (IntelliJ Platform Extension) extension.
Use the intellijPlatform.sandboxContainer ("sandboxContainer" in "IntelliJ Platform Extension").
Use the repositories {} block to manage repositories instead.
Downloading sources is managed by the Plugin DevKit plugin in version 2024.1+.
Access the ProductInfo ("ProductInfo" in "Types") object using the intellijPlatform.productInfo ("ProductInfo" in "Types") property.
The Robot Server Plugin integration is not yet available. See testIdeUi ("testIdeUi" in "Tasks").
Use testIdeUi ("testIdeUi" in "Tasks").
The task for running the IntelliJ Plugin Verifier is now called verifyPlugin ("verifyPlugin" in "Tasks").
Use intellijPlatform.verifyPlugin ("Verify Plugin" in "IntelliJ Platform Extension") extension to configure it.
To make the IntelliJ SDK dependency available in the IDE, the setupDependencies task was provided by Gradle IntelliJ Plugin 1.x. This task is no longer required, but when switching from 1.x, Gradle may still want to execute it in the afterSync phase. To completely drop this approach, it is mandatory to remove its reference manually in the IDE.
Open Gradle Tool Window
Right-click on the main module and select Tasks Activation
In the Tasks Activation modal window, find and remove the setupDependencies entry.
Add an explicit dependency on the plugin (https://github.com/JetBrains/gradle-idea-ext-plugin) in build.gradle.kts:
plugins {
id("org.jetbrains.gradle.plugin.idea-ext") version "1.1.8"
}runIde ("runIde" in "Tasks") task is a JavaExec (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) task and can be modified according to the documentation.
To add some JVM arguments while launching the IDE, configure runIde ("runIde" in "Tasks") task as follows:
tasks {
runIde {
jvmArgs("-DmyProperty=value")
}
}Using the very same task documentation (https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html), configure runIde ("runIde" in "Tasks") task:
tasks {
runIde {
systemProperty("name", "value")
}
}See "Enabling Auto-Reload" in "IDE Development Instance" for important caveats.
You can disable auto-reload globally with intellijPlatform.autoReload ("autoReload" in "IntelliJ Platform Extension"):
intellijPlatform {
autoReload = false
}It is also possible to disable it for a specific runIde ("runIde" in "Gradle IntelliJ Plugin")-based task as follows:
tasks {
runIde {
autoReload = false
}
}Building searchable options can be disabled using intellijPlatform.buildSearchableOptions ("instrumentCode" in "IntelliJ Platform Extension"):
intellijPlatform {
buildSearchableOptions = false
}As a result of disabling building searchable options, the Settings (Settings) that your plugin provides won't be searchable in the Settings dialog. Disabling of the task is suggested for plugins that are not intended to provide custom settings.
The most convenient way to see the logs of a running IDE is to add a tab to the Run tool window displaying the contents of idea.log file. In the Gradle runIde run configuration, add the log file path according to sandbox location ("The Development Instance Sandbox Directory" in "IDE Development Instance") as described in View logs (https://www.jetbrains.com/help/idea/setting-log-options.html).
The setupDependencies ("setupDependencies" in "Gradle IntelliJ Plugin") task was designed to fetch the target IntelliJ Platform dependency in the after-sync Gradle phase as a workaround for the Gradle IntelliJ Plugin 1.x limitations. Starting with the IntelliJ Platform Gradle Plugin 2.0, this task is no longer needed and was removed from available tasks.
Unfortunately, this entry may still remain right after the migration to 2.0 and cause the following exception:
Task 'setupDependencies' not found in root project 'projectName'.There are two possible solutions:
manually edit the .idea/workspace.xml file and remove the setupDependencies entry
open the Gradle tool window, select the Tasks Activation action from the context menu of the root project item, and remove the setupDependencies entry
See the Bundling Plugin API Sources section for details.
The Gradle IntelliJ Plugin, when targeting the IntelliJ SDK 2022.1+, uses the PathClassLoader class loader by the following system property:
-Djava.system.class.loader=com.intellij.util.lang.PathClassLoaderBecause of that, JaCoCo – and other external tools that rely on classes available in the bootstrap class loader – fail to discover plugin classes.
In addition, if the code instrumentation is enabled (see intellij.instrumentCode ("instrumentCode" in "IntelliJ Platform Extension")), it's required to switch to the compiled and instrumented output instead of default compiled classes.
The following changes to your Gradle configuration file:
tasks {
withType<Test> {
configure<JacocoTaskExtension> {
isIncludeNoLocationClasses = true
excludes = listOf("jdk.internal.*")
}
}
jacocoTestReport {
classDirectories.setFrom(instrumentCode)
}
jacocoTestCoverageVerification {
classDirectories.setFrom(instrumentCode)
}
}test {
jacoco {
includeNoLocationClasses = true
excludes = ["jdk.internal.*"]
}
}
jacocoTestReport {
classDirectories.setFrom(instrumentCode)
}
jacocoTestCoverageVerification {
classDirectories.setFrom(instrumentCode)
}Please upgrade to Kotlin 1.9.0. See the "Incremental compilation" in "Configuring Kotlin Support" section if using Kotlin 1.8.20.
To list the IntelliJ Platform releases matching your criteria (IntelliJ Platform type, release channels, or build range), you may use the "printProductsReleases" in "Tasks" task, as follows:
tasks {
printProductsReleases {
channels = listOf(ProductRelease.Channel.EAP)
types = listOf(IntelliJPlatformType.IntellijIdeaCommunity)
untilBuild = provider { null }
doLast {
val latestEap = productsReleases.get().max()
}
}
}When running tests or IDE with your plugin loaded, it is necessary to use JetBrains Runtime (JBR). In the case, no JBR is found in the plugin configuration, there's the following warning logged by the verifyPluginProjectConfiguration ("verifyPluginProjectConfiguration" in "Tasks") task:
The currently selected Java Runtime is not JetBrains Runtime (JBR).
This may lead to unexpected IDE behaviors.
Please use IntelliJ Platform binary release with bundled JBR
or define it explicitly with project dependencies or JVM Toolchain.To correctly run your tests or a specific IDE:
use a binary IDE distribution with bundled JetBrains Runtime, i.e., by referring to a local IDE local(localPath) ("Custom Target Platforms" in "Dependencies Extension")
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
}
}
dependencies {
intellijPlatform {
local("/Users/hsz/Applications/IntelliJ IDEA Ultimate.app")
}
}add an explicit dependency on a JetBrains Runtime with jetbrainsRuntime() ("Java Runtime" in "Dependencies Extension")
repositories {
mavenCentral()
intellijPlatform {
defaultRepositories()
jetbrainsRuntime()
}
}
dependencies {
intellijPlatform {
intellijIdeaCommunity("2024.1")
jetbrainsRuntime(...)
}
}specify the vendor when configuring the JVM Toolchain along with Foojay Toolchains Plugin (https://github.com/gradle/foojay-toolchains):
kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(17)
vendor = JvmVendorSpec.JETBRAINS
}
}Something missing?If a topic you are interested in is not covered in the above sections, let us know via the Was this page helpful? feedback form below or other channels ("Problems with the Guide" in "Getting Help").
Please be specific about the topics and reasons for adding them, and leave your email in case we need more details. Thanks for your feedback!
Current Release: 2022.3.2.2
GitHub: Releases & Changelog (https://github.com/JetBrains/gradle-grammar-kit-plugin/releases), Issue Tracker (https://github.com/JetBrains/gradle-grammar-kit-plugin/issues)
The Gradle Grammar-Kit Plugin (https://github.com/JetBrains/gradle-grammar-kit-plugin) automates generating lexers and parsers to support building custom language (Custom Language Support) plugins for IntelliJ-based IDEs when using Grammar-Kit (https://github.com/JetBrains/Grammar-Kit).
Known LimitationsThe plugin does not support two-pass generation. Therefore, it does not support method mixins.
Please see CONTRIBUTING (https://github.com/JetBrains/gradle-grammar-kit-plugin/blob/master/CONTRIBUTING.md) on how to submit feedback and contribute to this project.
Before visiting the Issue Tracker (https://github.com/JetBrains/gradle-grammar-kit-plugin/issues), update both plugin and Gradle to the latest versions.
To enable this plugin in your Gradle-based project, register the plugin in the Gradle build script's plugins section:
plugins {
id("org.jetbrains.grammarkit") version "2022.3.2.2"
}plugins {
id "org.jetbrains.grammarkit" version "2022.3.2.2"
}Minimum Gradle VersionThis project requires Gradle 7.4 or newer, however, it is recommended to use the latest Gradle available. See Gradle Installation (https://gradle.org/install/) guide.
See also below.
After the Gradle Grammar-Kit Plugin is applied, the grammarKit extension can be used to configure the plugin and common settings of the provided tasks. In most cases, explicit configuration can be omitted.
Example:
grammarKit {
jflexRelease.set("1.7.0-1")
grammarKitRelease.set("2021.1.2")
intellijRelease.set("203.7717.81")
}grammarKit {
jflexRelease = "1.7.0-1"
grammarKitRelease = "2021.1.2"
intellijRelease = "203.7717.81"
}The release version of the Grammar-Kit (https://github.com/JetBrains/Grammar-Kit) to use.
String
2022.3.2
The version of the IntelliJ-patched JFlex, a fork of JFlex (https://github.com/JetBrains/intellij-deps-jflex) lexer generator for IntelliJ Platform API.
String
1.9.2
An optional IntelliJ version to build the classpath for GenerateParser and GenerateLexer tasks.
If provided, grammarKitRelease and jflexRelease properties are ignored as both dependencies will be provided from the given IntelliJ IDEA release.
String
null
The generateLexer task generates a lexer for the given grammar. The task is configured using common the grammarKit extension.
The source .*flex file to generate the lexer from.
true
String
The path to the target directory for the generated lexer.
true
String
The Java file name where the generated lexer will be written.
true
String
An optional path to the skeleton file to use for the generated lexer. The path will be provided as --skel option. By default, it uses the idea-flex.skeleton (https://raw.github.com/JetBrains/intellij-community/master/tools/lexer/idea-flex.skeleton) skeleton file.
String
null
Purge old files from the target directory before generating the lexer.
Boolean
false
The generateParser task generates a parser for the given grammar. The task is configured using the common grammarKit extension.
The source .bnf file to generate the parser from.
true
String
The path to the target directory for the generated parser.
String
null
The location of the generated parser class, relative to the targetRoot.
true
String
The location of the generated PSI files, relative to the targetRoot.
true
String
Purge old files from the target directory before generating the parser.
Boolean
false
Grammar-Kit (https://github.com/JetBrains/Grammar-Kit)
IntelliJ-patched JFlex Sources (https://github.com/JetBrains/intellij-deps-jflex)
IntelliJ-patched JFlex (https://cache-redirector.jetbrains.com/intellij-dependencies/org/jetbrains/intellij/deps/jflex/jflex/)
The Tools | Internal Actions menu provides plugin developers with a suite of tools to help develop, debug, and test their IntelliJ Platform plugins.
If the menu item Tools | Internal Actions is not available in the IDE, then the first step is Enabling Internal Mode (Enabling Internal Mode).
Click on the following topics to learn more about the Internal Actions menu.
Enabling Internal Mode (Enabling Internal Mode) provides instructions for enabling the Internal Actions menu.
UI Tools (Internal Actions - UI Submenu) has information about some tools for inspecting and testing plugin UI.
There are useful tools, such as the Internal Actions menu, that are only visible if the internal mode is enabled in the IDE.
Start the IDE.
From the main menu, select Help | Edit Custom Properties.... This selection opens idea.properties file. If it does not exist, the IDE will prompt to create one.
Add the line shown below to the idea.properties file:
idea.is.internal=trueSave the idea.properties file and restart the IDE.
The Internal Actions menu is now available in Tools | Internal Actions.
The Internal Actions UI submenu provides IntelliJ Platform plugin developers with a suite of tools to help develop, debug, and test their IntelliJ Platform project UI.
If the menu item Tools | Internal Actions is not available in the IDE, then the first step is Enabling Internal Mode (Enabling Internal Mode).
Here are some tools available on the UI submenu of the Internal Actions menu:
UI Inspector (Internal Actions - UI Inspector) is a tool to get an internal description with each UI element's properties.
LaF Defaults (Internal Actions - LaF Defaults) provides a way to lookup the key-value pair for a UI element, and the ability to prototype the color of UI Controls.
The UI Inspector is a tool to interrogate elements of the IDE's UI to get an internal description of each element.
If the menu item Tools | Internal Actions is not available in the IDE, then the first step is Enabling Internal Mode (Enabling Internal Mode).
This step isn't required when using 2021.1 release or later.
Before using the UI Inspector, it must be enabled by selecting the menu item Tools | Internal Actions | UI | UI Inspector. The enabled state of the UI Inspector is modal; it remains enabled until it is disabled by selecting the menu item again.
Centering the cursor on a UI element and pressing CtrlAlt (CtrlOption on macOS) when clicking the left mouse button reveals the properties of the Swing component.
For example, to get information about the Build Project button's "hammer" icon on the toolbar (highlighted in green), put the mouse cursor on the icon and press Ctrl/CmdAlt while clicking the mouse.
The UI Inspector displays the icon details:

Sometimes, inspecting complex component's properties is not enough to understand how the component was created and configured. UI Inspector gives the possibility of finding the code where the selected component was added, which makes it much easier to understand which APIs can be used to build custom components with similar complexity.
To find the place were component was added, select the added-at property to show the stacktrace:

Various components used in the IntelliJ Platform expose additional properties. These can be useful to locate the underlying implementation, related Action, etc.
Custom Swing components can also provide additional properties via UiInspectorContextProvider (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/internal/inspector/UiInspectorContextProvider.java) or its dedicated subclasses (2020.1 and later).
Type | Properties |
|---|---|
AnAction (Actions) | Action - AnAction (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java) implementation Action ID - Action id Action Plugin ID - contributing plugin |
ActionToolbar (Actions) | Toolbar Group - Action Group ID All Toolbar Groups - contained Action Group IDs Target component - ActionToolbar.setTargetComponent() |
Type | Properties |
|---|---|
DialogWrapper (Dialogs) | dialogWrapperClass - DialogWrapper (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java) implementation |
ToolWindow (Tool Windows) | Tool Window ID - id Tool Window Icon - icon Tool Window Factory - ToolWindowFactory (https://github.com/JetBrains/intellij-community/tree/idea/241.14494.240/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowFactory.kt) |
Tree (List and Tree Controls) | treeModelClass - javax.swing.tree.TreeModel implementation |
Enable View | Appearance | Details in Tree View in Internal Mode (Enabling Internal Mode) to show Settings page id in the tree.
Some additional properties are available when inspecting Settings dialog (2023.1+).
UI Inspector must be invoked only after opening the Settings dialog.
The LaF Defaults window provides a key-value pair lookup for UI Controls. It also allows interactive prototyping of UI Control color changes.
LaF stands for Look and Feel, see Swing Tutorial (https://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/index.html) for more details.
If the menu item Tools | Internal Actions is not available in the IDE, then the first step is Enabling Internal Mode (Enabling Internal Mode).
The LaF Defaults window is opened by selecting the menu item Tools | Internal Actions | UI | LaF Defaults.
It has two columns representing key-value pairs for UI Controls:
The Name column contains the UI Control key for each IntelliJ Platform UI element available at runtime.
The Value column contains the UI Control color value for each IntelliJ Platform UI element.
The LaF Defaults window is used interactively by entering a UI element type - e.g. Panel - in the Filter text box at the top. LaF Defaults shows the list of UI Control names matching the filter.
Enable Colors only to show only entries with color value.
Clicking on one of the names narrows the information to show only the key-value pair for that UI element:

The color of UI Controls can be changed (in real time) by clicking in the Value column next to a Name (key) of interest. The Choose Color window is displayed. Color changes can be specified as RGB, HSB, hexadecimal, or using the graphical color picker. Pressing the Choose button changes the UI Control color immediately.
UI Control colors can be reset using the Choose Color window, or by resetting the theme (https://www.jetbrains.com/help/idea/settings-appearance.html).